Search code examples
c++exceptiontestingautomated-testsgoogletest

Can I know which exception was thrown inside a gtest EXPECT_NO_THROW (or ASSERT_NO_THROW)?


To test my C++ project I am using GoogleTest framework. Normally I can use the following syntax to easily debug a failure:

EXPECT_TRUE(*statement*) << *debugMessage*;

When I use the macro EXPECT_NO_THROW (or ASSERT_NO_THROW) I might of course do the same, but I do not have access to the exception object that was thrown (and caught) inside the macro itself and so the debugMessage cannot tell me anything about it.

Is it possible to show information about this exception in any way?

EDIT

Is not possible without any custom function/macro.


Solution

  • Here's one way:

    #include <exception>
    #include <stdexcept>
    #include <ostream>
    #include <iostream> // for the test
    #include <gtest/gtest.h>
    namespace detail {
    
        struct unwrapper
        {
            unwrapper(std::exception_ptr pe) : pe_(pe) {}
    
            operator bool() const {
                return bool(pe_);
            }
    
            friend auto operator<<(std::ostream& os, unwrapper const& u) -> std::ostream&
            {
                try {
                    std::rethrow_exception(u.pe_);
                    return os << "no exception";
                }
                catch(std::runtime_error const& e)
                {
                    return os << "runtime_error: " << e.what();
                }
                catch(std::logic_error const& e)
                {
                    return os << "logic_error: " << e.what();
                }
                catch(std::exception const& e)
                {
                    return os << "exception: " << e.what();
                }
                catch(...)
                {
                    return os << "non-standard exception";
                }
    
            }
            std::exception_ptr pe_;
        };
    
    }
    
    auto unwrap(std::exception_ptr pe)
    {
        return detail::unwrapper(pe);
    }
    
    
    template<class F>
    ::testing::AssertionResult does_not_throw(F&& f)
             {
                 try {
                     f();
                     return ::testing::AssertionSuccess();
                 }
                 catch(...) {
                     return ::testing::AssertionFailure() << unwrap(std::current_exception());
                 }
             };
    
    
    TEST(a, b)
    {
        ASSERT_TRUE(does_not_throw([] { throw std::runtime_error("i threw"); }));
    }
    

    example output:

    Running main() from gtest_main.cc
    [==========] Running 1 test from 1 test case.
    [----------] Global test environment set-up.
    [----------] 1 test from a
    [ RUN      ] a.b
    /Users/rhodges/play/project/nod.cpp:66: Failure
    Value of: does_not_throw([] { throw std::runtime_error("i threw"); })
      Actual: false (runtime_error: i threw)
    Expected: true
    [  FAILED  ] a.b (1 ms)
    [----------] 1 test from a (1 ms total)
    
    [----------] Global test environment tear-down
    [==========] 1 test from 1 test case ran. (1 ms total)
    [  PASSED  ] 0 tests.
    [  FAILED  ] 1 test, listed below:
    [  FAILED  ] a.b
    
     1 FAILED TEST