测试抛出的特定异常类型,并且异常具有正确的属性

时间:2014-04-24 13:16:43

标签: c++ unit-testing exception googletest

我想测试在某种情况下抛出MyExceptionEXPECT_THROW在这里很好。但我还想检查异常是否具有特定状态,例如e.msg() == "Cucumber overflow"

如何在GTest中实现最佳效果?

12 个答案:

答案 0 :(得分:42)

我主要是第二个Lilshieste的答案,但会补充说你也应该这样做 验证是否未抛出错误的异常类型:

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

答案 1 :(得分:29)

一位同事通过重新抛出异常来提出解决方案。

诀窍:不需要额外的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 );
}

答案 2 :(得分:15)

Jeff Langr在他的书Modern C++ Programming with Test-Driven Development中描述了一种很好的方法:

  

如果您的[testing]框架不支持确保抛出异常的单行声明性断言,则可以在测试中使用以下结构:

    TEST(ATweet, RequiresUserNameToStartWithAnAtSign) {
        string invalidUser("notStartingWith@");
        try {
            Tweet tweet("msg", invalidUser);
            FAIL();
        }
        catch(const InvalidUserException& expected) {}
    }
     

[...] 如果必须在抛出异常后验证任何后置条件,则可能还需要使用try-catch结构。例如,您可能希望验证与抛出的异常对象关联的文本。

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

(第95页)

这是我使用过的方法,并且已经在其他地方看到了。

编辑正如@MikeKinghan所指出的那样,完全不匹配EXPECT_THROW提供的功能;如果抛出错误的异常,测试不会失败。可以添加一个额外的catch子句来解决这个问题:

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

答案 3 :(得分:2)

我的版本;它产生与EXPECT_THROW相同的输出,只是添加了字符串test:

#define EXPECT_THROW_MSG(statement, expected_exception, expected_what)                    \
  try                                                                                     \
  {                                                                                       \
    statement;                                                                            \
    FAIL() << "Expected: " #statement " throws an exception of type " #expected_exception \
              ".\n"                                                                       \
              "  Actual: it throws nothing.";                                             \
  }                                                                                       \
  catch (const expected_exception& e)                                                     \
  {                                                                                       \
    EXPECT_EQ(expected_what, std::string{e.what()});                                      \
  }                                                                                       \
  catch (...)                                                                             \
  {                                                                                       \
    FAIL() << "Expected: " #statement " throws an exception of type " #expected_exception \
              ".\n"                                                                       \
              "  Actual: it throws a different type.";                                    \
  }

答案 4 :(得分:1)

我建议根据Mike Kinghan的方法定义一个新的宏。

#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  \
           << "'!";                                                   \
}

Mike的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 :(得分:1)

由于我需要做几个这样的测试,我写了一个宏,基本上包括Mike Kinghan的答案,但是&#34;删除&#34;所有样板代码:

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

注意事项:

  • 由于宏在当前作用域中定义了一个变量,所以它只能使用一次。
  • std::exception_ptr
  • 需要C ++ 11

答案 6 :(得分:0)

我喜欢大多数答案。但是,由于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 :(得分:0)

我使用MatthäusBrandl的宏进行了以下微小修改:

放线

std::exception_ptr _exceptionPtr;

外部(f.e. before)宏定义为

static std::exception_ptr _exceptionPtr;

避免对符号_exceptionPtr进行多重定义。

答案 8 :(得分:0)

扩展先前的答案,该宏用于验证是否抛出了给定类型的异常,并且其消息以提供的字符串开头。

如果没有引发异常,异常类型错误或消息不是以提供的字符串开头,则测试失败。

#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 :(得分:0)

我建议使用

EXPECT_THROW(function_that_will_throw(), exception);

如果你的函数返回无效的东西:

EXPECT_THROW((void)function_that_will_throw(), exception);

答案 10 :(得分:0)

我之前在 an older answer 中提供了一个宏来解决这个问题。然而,随着时间的流逝,GTest 中添加了一项新功能,无需宏即可实现这一点。

该特征是一组匹配器,例如,可以与 Throws 结合使用的 EXPECT_THAT()。但是文档似乎没有更新,所以唯一的信息隐藏在 this GitHub issue 中。


该功能是这样使用的:

EXPECT_THAT([]() { throw std::runtime_error("message"); },
    Throws<std::runtime_error>());

EXPECT_THAT([]() { throw std::runtime_error("message"); },
    ThrowsMessage<std::runtime_error>(HasSubstr("message")));

EXPECT_THAT([]() { throw std::runtime_error("message"); },
    ThrowsMessageHasSubstr<std::runtime_error>("message"));

EXPECT_THAT([]() { throw std::runtime_error("message"); },
    Throws<std::runtime_error>(Property(&std::runtime_error::what,
         HasSubstr("message"))));

请注意,由于 EXPECT_THAT() 的工作方式,您需要将 throwing 语句放入无需参数即可调用的内容中。因此就是上面例子中的 lambda。


另请注意,此功能未包含在 1.10 版中,但已合并到 master 中。因为 GTest 遵循绳索的现场直播政策,所以目前有 are no new versions planned。此外,他们似乎没有遵循 abseil 的政策,为那些不能/不会生活在头脑中的人发布特定版本。

答案 11 :(得分:-1)

您可以尝试Boost轻量级测试:

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