Domanda

When I test a method using

BOOST_CHECK_NO_THROW( method_to_test() );

and an exception is thrown, it displays that an exception was thrown, but never the exception's message like this:

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

Is it possible to print the exception message as well, i.e. the string returned by my_exception.what()? my_exception is derived from std::exception and overloads what().

È stato utile?

Soluzione 2

I read a bit in the boost headers and redefined BOOST_CHECK_NO_THROW_IMPL in my own header file I use in the project to redefine the boost behavior. Now it looks like this:

#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_

The disadvantages are: It works as long as there are no changes in BOOST_*_NO_THROW

and

the exception message will be printed before it is marked as error in the test output. That looks in the first place a bit pitty, that's why I group the output by writing "---" to the outstream to enhance the reading. But code after BOOST_CHECK_IMPL will never be reached.

The solution above work quite nice for me. Feel free to use to, if you got the same whish =)

(Using CDash for ctest output, don't forget to increase the test output limit, or simple disable the limit: http://web.archiveorange.com/archive/v/5y7PkVuHtkmVcf7jiWol )

Altri suggerimenti

I found myself annoyed by the same problem with BOOST_REQUIRE_NO_THROW. I solved it by simply removing the BOOST_REQUIRE_NO_THROW. This results in output like:

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

and aborts the test (but goes on with the next text), which is what I wanted. This doesn't help much if you wanted to use BOOST_CHECK_NO_THROW or BOOST_WARN_NO_THROW, though.

Solution 1.

Use a wrapper method that catches the exception, then prints the error message and then re-throws so that BOOST can report it:

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;
    }
}

Then your test case would read:

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

A drawback of this approach is that you will need to use a different middle_man function for every method_to_test.

Solution 2.

Use decorators, see this answer, to do just what a wrapper from the previous solution would do.

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

then your test cases would look like this:

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top