Reputation: 125
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.
Upvotes: 5
Views: 7628
Reputation: 156
I am a little bit late to the party, but here's one approach which works:
/**
* @brief Wrap a code block with try-catch, handle exceptions thrown, print them
* into EXCEPT_STREAM and rethrow.
*/
#define PRINT_AND_RETHROW(CODE_BLOCK, EXCEPT_STREAM) try{do{ CODE_BLOCK }while(0);}catch(const std::exception& ex){ EXCEPT_STREAM << "std::exception thrown: " << ex.what() << std::endl; throw; }catch(...){ EXCEPT_STREAM << "unknown structure thrown" << std::endl; throw;}
/**
* @brief Wrap a code block with try-catch, handle exceptions thrown, print them
* into std::cerr and rethrow.
*/
#define PRINT_STDERR_AND_RETHROW(CODE_BLOCK) PRINT_AND_RETHROW(CODE_BLOCK, std::cerr)
#define EXPECT_NO_THROW_PRINT(CODE_BLOCK) EXPECT_NO_THROW(SPECTRE_PRINT_STDERR_AND_RETHROW(CODE_BLOCK))
#define ASSERT_NO_THROW_PRINT(CODE_BLOCK) ASSERT_NO_THROW(SPECTRE_PRINT_STDERR_AND_RETHROW(CODE_BLOCK))
Later in code, replace *_NO_THROW
's with *_NO_THROW_PRINT
and voila.
void f(){
throw std::runtime_error{"this should be printed"};
}
TEST(test, test){
EXPECT_NO_THROW_PRINT( f(); );
}
Expected output of test case above:
Running main() from /build/googletest-qXr8Ln/googletest-1.10.0/googletest/src/gtest_main.cc
Note: Randomizing tests' orders with a seed of 60645 .
[==========] Running 2 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 1 test from test
[ RUN ] test.test
std::exception thrown: this should be printed
/workspace/libasync/test/ut_executor_factory.cpp:56: Failure
Expected: try{do{ f(); }while(0);}catch(const std::exception& ex){ std::cerr << "std::exception thrown: " << ex.what() << std::endl; throw; }catch(...){ std::cerr << "unknown structure thrown" << std::endl; throw;} doesn't throw an exception.
Actual: it throws.
[ FAILED ] test.test (0 ms)
[----------] 1 test from test (0 ms total)
Upvotes: 0
Reputation: 1829
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
Upvotes: 4
Reputation: 69892
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
Upvotes: 5