Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 967
San Mosy Avatar asked Mar 10 '23 13:03

San Mosy


2 Answers

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
like image 97
Richard Hodges Avatar answered Apr 06 '23 00:04

Richard Hodges


An alternative to the answer by Richard Hodges, is to use a try-catch structure inside the test-body. This solution comes from the very good book Modern C++ Programming with Test-Driven Development written by Jeff Langr.

A complete working example could look like the following:

#include <stdexcept>
#include "gtest/gtest.h"

struct foo
{
  void bar() {
    throw std::runtime_error("unexpected error");
  }
};

TEST(foo_test, does_not_throw)
{
  foo f;
  try {
    f.bar();
    SUCCEED();
  }
  catch (std::exception const & err) {
    FAIL() << err.what();
  }
}

int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

And the ouput:

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from foo_test
[ RUN      ] foo_test.does_not_throw
    /Users/Soeren/Documents/cmakeProject/src/applications/modelTest/main.cpp(26): error: Failed
unexpected error messages
[  FAILED  ] foo_test.does_not_throw (1 ms)
[----------] 1 test from foo_test (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (3 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] foo_test.does_not_throw

 1 FAILED TEST
like image 29
Soeren Avatar answered Apr 05 '23 22:04

Soeren