Подтвердить что ты не робот

Проверяется конкретный тип исключения, и исключение имеет правильные свойства

Я хочу проверить, что MyException выбрано в определенном случае. EXPECT_THROW здесь хорошо. Но я также хочу проверить, что исключение имеет конкретное состояние, например e.msg() == "Cucumber overflow".

Как это лучше всего реализовать в GTest?

4b9b3361

Ответ 1

Я в основном второй ответ Лильшиэста, но добавлю, что вы также должны убедитесь, что неправильный тип исключения не выбрасывается:

#include <stdexcept>
#include "gtest/gtest.h"

struct foo
{
    int bar(int i) {
        if (i > 100) {
            throw std::out_of_range("Out of range");
        }
        return i;
    }
};

TEST(foo_test,out_of_range)
{
    foo f;
    try {
        f.bar(111);
        FAIL() << "Expected std::out_of_range";
    }
    catch(std::out_of_range const & err) {
        EXPECT_EQ(err.what(),std::string("Out of range"));
    }
    catch(...) {
        FAIL() << "Expected std::out_of_range";
    }
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Ответ 2

Сотрудник придумал решение, просто перекинув исключение.

Уловка: нет необходимости в дополнительных операциях FAIL(), только два вызова EXPECT..., которые проверяют нужные вам биты: исключение как таковое и его значение.

TEST(Exception, HasCertainMessage )
{
    // this tests _that_ the expected exception is thrown
    EXPECT_THROW({
        try
        {
            thisShallThrow();
        }
        catch( const MyException& e )
        {
            // and this tests that it has the correct message
            EXPECT_STREQ( "Cucumber overflow", e.what() );
            throw;
        }
    }, MyException );
}

Ответ 3

Джефф Лэнгр описывает хороший подход в своей книге: Современное программирование на С++ с тестовым развитием:

Если ваша [тестовая] среда не поддерживает однострочное декларативное утверждение, обеспечивающее исключение, вы можете использовать следующую структуру в своем тесте:

    TEST(ATweet, RequiresUserNameToStartWithAnAtSign) {
        string invalidUser("[email protected]");
        try {
            Tweet tweet("msg", invalidUser);
            FAIL();
        }
        catch(const InvalidUserException& expected) {}
    }

[...] Вам также может потребоваться использовать структуру try-catch, если вы должны проверить любые постусловия после того, как будет выбрано исключение. Например, вы можете проверить текст, связанный с созданным объектом исключения.

    TEST(ATweet, RequiresUserNameToStartWithAtSign) {
        string invalidUser("[email protected]");
        try {
            Tweet tweet("msg", invalidUser);
            FAIL();
        }
        catch(const InvalidUserException& expected) {
            ASSERT_STREQ("[email protected]", expected.what());
        }
    }

(с .95)

Это тот подход, который я использовал, и видел на практике в другом месте.

Изменить: Как было указано в @MikeKinghan, это не совсем соответствует функциональности, предоставляемой EXPECT_THROW; тест не прерывается, если выбрано неправильное исключение. Для этого можно добавить дополнительное предложение catch:

catch(...) {
    FAIL();
}

Ответ 4

Я рекомендую определить новый макрос на основе подхода Майка Кингхана.

#define ASSERT_EXCEPTION( TRY_BLOCK, EXCEPTION_TYPE, MESSAGE )        \
try                                                                   \
{                                                                     \
    TRY_BLOCK                                                         \
    FAIL() << "exception '" << MESSAGE << "' not thrown at all!";     \
}                                                                     \
catch( const EXCEPTION_TYPE& e )                                      \
{                                                                     \
    EXPECT_EQ( MESSAGE, e.what() )                                    \
        << " exception message is incorrect. Expected the following " \
           "message:\n\n"                                             \
        << MESSAGE << "\n";                                           \
}                                                                     \
catch( ... )                                                          \
{                                                                     \
    FAIL() << "exception '" << MESSAGE                                \
           << "' not thrown with expected type '" << #EXCEPTION_TYPE  \
           << "'!";                                                   \
}

Пример Майка TEST(foo_test,out_of_range) был бы

TEST(foo_test,out_of_range)
{
    foo f;
    ASSERT_EXCEPTION( { f.bar(111); }, std::out_of_range, "Out of range" );
}

который, как мне кажется, становится более читаемым.

Ответ 5

Как мне нужно сделать несколько таких тестов, я написал макрос, который в основном включает ответ Майка Кингхана, но "удаляет" весь шаблонный код:

#define ASSERT_THROW_KEEP_AS_E(statement, expected_exception) \
    std::exception_ptr _exceptionPtr; \
    try \
    { \
        (statement);\
        FAIL() << "Expected: " #statement " throws an exception of type " \
          #expected_exception ".\n  Actual: it throws nothing."; \
    } \
    catch (expected_exception const &) \
    { \
        _exceptionPtr = std::current_exception(); \
    } \
    catch (...) \
    { \
        FAIL() << "Expected: " #statement " throws an exception of type " \
          #expected_exception ".\n  Actual: it throws a different type."; \
    } \
    try \
    { \
        std::rethrow_exception(_exceptionPtr); \
    } \
    catch (expected_exception const & e)

Использование:

ASSERT_THROW_KEEP_AS_E(foo(), MyException)
{
    ASSERT_STREQ("Cucumber overflow", e.msg());
}

Предостережение:

  • Поскольку макрос определяет переменную в текущей области, поэтому ее можно использовать только один раз.
  • С++ 11 необходим для std::exception_ptr

Ответ 6

Мне нравится большинство ответов. Однако, поскольку кажется, что GoogleTest предоставляет EXPECT_PRED_FORMAT, что помогает в этом, я хотел бы добавить этот вариант в список ответов:

MyExceptionCreatingClass testObject; // implements TriggerMyException()

EXPECT_PRED_FORMAT2(ExceptionChecker, testObject, "My_Expected_Exception_Text");

где ExceptionChecker определяется как:

testing::AssertionResult ExceptionChecker(const char* aExpr1,
                                          const char* aExpr2,
                                          MyExceptionCreatingClass& aExceptionCreatingObject,
                                          const char* aExceptionText)
{
  try
  {
    aExceptionCreatingObject.TriggerMyException();
    // we should not get here since we expect an exception
    return testing::AssertionFailure() << "Exception '" << aExceptionText << "' is not thrown.";
  }
  catch (const MyExpectedExceptionType& e)
  {
    // expected this, but verify the exception contains the correct text
    if (strstr(e.what(), aExceptionText) == static_cast<const char*>(NULL))
    {
      return testing::AssertionFailure()
          << "Exception message is incorrect. Expected it to contain '"
          << aExceptionText << "', whereas the text is '" << e.what() << "'.\n";
    }
  }
  catch ( ... )
  {
    // we got an exception alright, but the wrong one...
    return testing::AssertionFailure() << "Exception '" << aExceptionText
    << "' not thrown with expected type 'MyExpectedExceptionType'.";
  }
  return testing::AssertionSuccess();
}

Ответ 7

Я использую макрос Matthäus Brandl со следующей незначительной модификацией:

Поместите строку

std::exception_ptr _exceptionPtr;

снаружи (например, раньше) определение макроса как

static std::exception_ptr _exceptionPtr;

чтобы избежать множественного определения символа _exceptionPtr.

Ответ 8

Расширяя предыдущие ответы, макрос, который проверяет, что было сгенерировано исключение данного типа, и сообщение о котором начинается с указанной строки.

Тест не пройден, если либо не сгенерировано исключение, либо неверный тип исключения, либо сообщение не начинается с указанной строки.

#define ASSERT_THROWS_STARTS_WITH(expr, exc, msg) \
    try\
    {\
            (expr);\
            FAIL() << "Exception not thrown";\
    }\
    catch (const exc& ex)\
    {\
            EXPECT_THAT(ex.what(), StartsWith(std::string(msg)));\
    }\
    catch(...)\
    {\
            FAIL() << "Unexpected exception";\
    } 

Пример использования:

ASSERT_THROWS_STARTS_WITH(foo(-2), std::invalid_argument, "Bad argument: -2");

Ответ 9

Вы можете попробовать легкий легкий тест:

#include <boost/detail/lightweight_test.hpp>
#include <stdexcept>

void function_that_would_throw(int x)
{
  if (x > 0) {
    throw std::runtime_error("throw!");
  }
}

int main() {
 BOOST_TEST_THROWS(function_that_would_throw(10), std::runtime_error);
 boost::report_errors();
}