我能知道在 gtest EXPECT_NO_THROW(或 ASSERT_NO_THROW)中抛出了哪个异常吗?

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

为了测试我的 C++ 项目,我使用了 GoogleTest 框架。 通常我可以使用以下语法轻松调试故障:

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

当我使用宏 EXPECT_NO_THROW(或 ASSERT_NO_THROW)时,我当然可以这样做,但我无法访问在宏本身,因此 debugMessage 无法告诉我任何有关它的信息。

是否可以以任何方式显示有关此异常的信息?

编辑

没有任何自定义是不可能的 function/macro。

这是一种方法:

#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"); }));
}

示例输出:

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

Richard Hodges 的答案的替代方法是在测试体内使用 try-catch 结构。这个解决方案来自 Jeff Langr 写的非常好的书 Modern C++ Programming with Test-Driven Development

一个完整的工作示例如下所示:

#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();
}

输出:

[==========] 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

我来晚了一点,但这是一种有效的方法:

/**
 * @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))

稍后在代码中,将 *_NO_THROW 替换为 *_NO_THROW_PRINT 瞧。


void f(){
    throw std::runtime_error{"this should be printed"};
}

TEST(test, test){
    EXPECT_NO_THROW_PRINT( f(); );
}

上述测试用例的预期输出:

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)