San Mosy
San Mosy

Reputation: 125

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.

Upvotes: 5

Views: 7628

Answers (3)

Mustafa Kemal GILOR
Mustafa Kemal GILOR

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

Soeren
Soeren

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

Richard Hodges
Richard Hodges

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

Related Questions