10

当我使用

BOOST_CHECK_NO_THROW( method_to_test() );

并抛出异常,它显示抛出异常,但绝不会像这样的异常消息

test.cpp(14): error in "test": incorrect exception my_exception is caught

是否也可以打印异常消息,即返回的字符串my_exception.what()my_exception派生自std::exception和 重载what()

4

3 回答 3

8

我发现自己对BOOST_REQUIRE_NO_THROW. 我通过简单地删除它来解决它BOOST_REQUIRE_NO_THROW。这导致输出如下:

unknown location(0): fatal error in "TestName": std::runtime_error: Exception message

并中止测试(但继续下一个文本),这就是我想要的。但是,如果您想使用 BOOST_CHECK_NO_THROW 或 BOOST_WARN_NO_THROW,这并没有多大帮助。

于 2013-04-24T23:12:00.707 回答
6

我在 boost headers 中阅读了一些内容,并在我自己的头文件中重新定义了 BOOST_CHECK_NO_THROW_IMPL,我在项目中使用它来重新定义 boost 行为。现在看起来像这样:

#ifndef _CATCH_BOOST_NO_THROW_H_
#define _CATCH_BOOST_NO_THROW_H_  

#include <boost/test/unit_test.hpp>
#include <sstream>
#include <string>

#define BOOST_CHECK_NO_THROW_IMPL( S, TL )                                                      \
    try {                                                                                       \
    S;                                                                                          \
    BOOST_CHECK_IMPL( true, "no exceptions thrown by " BOOST_STRINGIZE( S ), TL, CHECK_MSG ); } \
    catch( const std::exception & e ) {                                                         \
    std::stringstream ss;                                                                       \
    ss << std::endl                                                                             \
    << "-----------------------------------------------" << std::endl                           \
    << "test case: " << boost::unit_test::framework::current_test_case().p_name << std::endl    \
    << std::endl << "exception message: " << e.what() << std::endl;                             \
    BOOST_TEST_MESSAGE(ss.str());                                                               \
    BOOST_CHECK_IMPL( false, "exception thrown by " BOOST_STRINGIZE( S ), TL, CHECK_MSG );      \
    }                                                                                           \
    catch( ... ) {                                                                              \
    std::stringstream ss;                                                                       \
    ss << std::endl                                                                             \
    << "-----------------------------------------------" << std::endl                           \
    << "test case: " << boost::unit_test::framework::current_test_case().p_name << std::endl    \
    << std::endl << "exception message : <unknown exception>" << std::endl;                     \
    BOOST_TEST_MESSAGE(ss.str());                                                               \
    BOOST_CHECK_IMPL( false, "exception thrown by " BOOST_STRINGIZE( S ), TL, CHECK_MSG );      \
    }                                                                                           \
    /**/

#define BOOST_WARN_NO_THROW( S )            BOOST_CHECK_NO_THROW_IMPL( S, WARN )
#define BOOST_CHECK_NO_THROW( S )           BOOST_CHECK_NO_THROW_IMPL( S, CHECK )
#define BOOST_REQUIRE_NO_THROW( S )         BOOST_CHECK_NO_THROW_IMPL( S, REQUIRE )

#endif // _CATCH_BOOST_NO_THROW_H_

缺点是:只要 BOOST_*_NO_THROW 没有变化,它就可以工作

异常消息将在测试输出中标记为错误之前打印。这首先看起来有点可怜,这就是为什么我通过将“---”写入 outstream 来对输出进行分组以增强阅读效果。但是 BOOST_CHECK_IMPL 之后的代码永远不会到达。

上面的解决方案对我来说非常好。随意使用,如果你有同样的愿望 =)

(使用CDash进行ctest输出,不要忘记增加测试输出限制,或者简单禁用限制:http ://web.archiveorange.com/archive/v/5y7PkVuHtkmVcf7jiWol )

于 2013-05-22T21:00:06.803 回答
0

解决方案 1。

使用捕获异常的包装器方法,然后打印错误消息,然后重新抛出以便BOOST可以报告它:

void method_to_test(int s)
{
    if(s==0)
        throw std::runtime_error("My error message");
}

void middle_man(int x)
{
    try
    {
        method_to_test(x);
    }catch(std::exception& e)
    {
        std::stringstream mes;
        mes << "Exception thrown: " << e.what();
        BOOST_TEST_MESSAGE(mes.str());// BOOST_ERROR(mes.str());
        throw;
    }
}

然后您的测试用例将显示为:

BOOST_AUTO_TEST_CASE(case1)
{
    BOOST_CHECK_NO_THROW( middle_man(0) );
}

这种方法的一个缺点是您需要middle_man为每个method_to_test.

解决方案 2。

使用装饰器,看到这个答案,来做之前解决方案中的包装器会做的事情。

template <class> struct Decorator;

template<class R,class ... Args>
struct Decorator<R(Args ...)>
{
    std::function<R(Args ...)> f_;
    
    Decorator(std::function<R(Args ...)> f):
        f_{f} 
    {}
    
    R operator()(Args ... args)
    {
        try
        {
            f_(args...);
        }catch(std::exception& e)
        {
            std::stringstream mes;
            mes << "Exception thrown: " << e.what();
            BOOST_TEST_MESSAGE(mes.str());
            throw;
        }
    }
};

template<class R,class ... Args>
Decorator<R(Args...)> makeDecorator(R (*f)(Args ...))
{
    return Decorator<R(Args...)>(std::function<R(Args...)>(f));
}

那么您的测试用例将如下所示:

BOOST_AUTO_TEST_CASE(case2)
{
    BOOST_CHECK_NO_THROW( makeDecorator(method_to_test)(0) );
    BOOST_CHECK_NO_THROW( makeDecorator(another_method_to_test)() );
}

于 2020-11-24T07:47:56.207 回答