BOOST_CHECK_NO_THROW, как распечатать сообщение об исключении

Когда я тестирую метод, используя

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().


person 550    schedule 28.02.2013    source источник
comment
Написать куда? Boost-тесты предназначены для проверки правильности кода, а не для его отладки. Он сломан? Это логическое значение: true или false.   -  person Öö Tiib    schedule 28.02.2013
comment
(1) Запись в стандартный вывод (консоль или xml-файл, в зависимости от аргументов командной строки запуска теста) (2) Если выдается исключение, да, оно сломано. Но если можно увидеть сообщение об исключении (вывод what()), то быстрее выяснить, откуда возникла ошибка.   -  person 550    schedule 28.02.2013
comment
Да, но почему? Тест действительно показал, что код сломан: он генерирует исключения там, где не должен. Следующий шаг — взять и исправить либо код, либо тест, а не создавать подробные XML-файлы заполнения консоли мусором.   -  person Öö Tiib    schedule 28.02.2013
comment
Хм, хорошо. Я подумал, что было бы неплохо быстрее найти ошибку. В моем тесте используется db (на самом деле нет фиктивного объекта), и тест может завершиться неудачей по разным причинам (не только из-за неправильного класса или кода теста). Я полагаю, что может быть решение из-за «широкой настраиваемой» среды для ускоренного тестирования, но я могу закончить без использования этого прекрасного BOOST_AUTO_*(). Но спасибо Тииб!   -  person 550    schedule 28.02.2013
comment
Почему, @ÖöTiib? Вы бы предпочли, чтобы ваш доктор сказал вам, что я определил проблему с вами. или я выявил проблему с коленом.? Среда модульного тестирования напечатала бы сообщение об исключении, если бы инструкция не была заключена в BOOST_<LEVEL>_NO_THROW. Использование утверждений не должно давать вам меньше информации, чем вы бы получили без них.   -  person Trebor Rude    schedule 25.04.2013


Ответы (3)


Меня раздражала та же проблема с 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.

person Trebor Rude    schedule 24.04.2013
comment
Как ни странно, это лучшее решение для меня. - person Archont; 26.06.2017

Я немного почитал заголовки Boost и переопределил 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

и

сообщение об исключении будет напечатано до того, как оно будет помечено как ошибка в тестовом выводе. Во-первых, это выглядит немного жалко, поэтому я группирую вывод, записывая «---» в исходящий поток, чтобы улучшить чтение. Но код после BOOST_CHECK_IMPL никогда не будет достигнут.

Решение выше работает довольно хорошо для меня. Не стесняйтесь использовать, если у вас есть такое же желание =)

(Используя CDash для вывода ctest, не забудьте увеличить предел вывода теста или просто отключить ограничение: http://web.archiveorange.com/archive/v/5y7PkVuHtkmVcf7jiWol )

person 550    schedule 22.05.2013

Решение 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)() );
}

person Lagrange.el.Ciencia    schedule 24.11.2020