I want to test that MyException
is thrown in a certain case. EXPECT_THROW
is good here. But I also want to check the exception has a specific state e.g e.msg() == "Cucumber overflow"
.
我想测试在特定情况下抛出的MyException。EXPECT_THROW是好的。但我也想检查异常有一个特定的状态e。g e.msg() =“黄瓜溢出”。
How is this best implemented in GTest?
如何在GTest中实现这一点?
8 个解决方案
#1
30
I mostly second Lilshieste's answer but would add that you also should verify that the wrong exception type is not thrown:
我主要支持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();
}
#2
14
Jeff Langr describes a good approach in his book, Modern C++ Programming with Test-Driven Development:
Jeff Langr在他的《使用测试驱动开发的现代c++编程》一书中描述了一种很好的方法:
If your [testing] framework does not support a single-line declarative assert that ensures an exception is thrown, you can use the following structure in your test:
如果您的[测试]框架不支持确保抛出异常的单行声明断言,那么您可以在测试中使用以下结构:
TEST(ATweet, RequiresUserNameToStartWithAnAtSign) { string invalidUser("notStartingWith@"); try { Tweet tweet("msg", invalidUser); FAIL(); } catch(const InvalidUserException& expected) {} }
[...] You might also need to use the try-catch structure if you must verify any postconditions after the exception is thrown. For example, you may want to verify the text associated with the thrown exception object.
[…如果在抛出异常后必须验证任何后置条件,您可能还需要使用try-catch结构。例如,您可能想要验证与抛出的异常对象关联的文本。
TEST(ATweet, RequiresUserNameToStartWithAtSign) { string invalidUser("notStartingWith@"); try { Tweet tweet("msg", invalidUser); FAIL(); } catch(const InvalidUserException& expected) { ASSERT_STREQ("notStartingWith@", expected.what()); } }
(p.95)
(p.95)
This is the approach I've used, and have seen in practice elsewhere.
这是我用过的方法,在实践中也见过。
Edit: As has been pointed out by @MikeKinghan, this doesn't quite match the functionality provided by EXPECT_THROW
; the test doesn't fail if the wrong exception is thrown. An additional catch
clause could be added to address this:
编辑:正如@MikeKinghan指出的,这与EXPECT_THROW提供的功能不太匹配;如果抛出错误的异常,测试不会失败。为了解决这个问题,可以增加附加的catch条款:
catch(...) {
FAIL();
}
#3
13
A colleague came up with the solution by just re-throwing the exception.
一个同事提出了解决方案,只是重新抛出了一个例外。
The knack: no need of extra FAIL() statements, just the two EXPECT... calls that test the bits you actually want: the exception as such and its value.
诀窍:不需要额外的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 );
}
#4
0
I recommend defining a new macro based on Mike Kinghan's approach.
我建议基于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's TEST(foo_test,out_of_range)
example would then be
接下来是Mike的测试(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" );
}
which I think ends up being much more readable.
我觉得这样更容易读懂。
#5
0
I like most of the answers. However, since it seems that GoogleTest provides EXPECT_PRED_FORMAT that helps facilitating this, I'd like to add this option to the list of answers:
我喜欢大部分的答案。然而,由于GoogleTest提供了EXPECT_PRED_FORMAT,这有助于实现这一目的,所以我想将这个选项添加到答案列表中:
MyExceptionCreatingClass testObject; // implements TriggerMyException()
EXPECT_PRED_FORMAT2(ExceptionChecker, testObject, "My_Expected_Exception_Text");
where ExceptionChecker is defined as:
其中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();
}
#6
0
As I need to do several of such tests I wrote a macro that basically includes Mike Kinghan's answer but "removes" all the boilerplate code:
当我需要做几个这样的测试时,我写了一个宏,基本上包含了Mike Kinghan的答案,但是“删除”了所有的样板代码:
#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)
Usage:
ASSERT_THROW_KEEP_AS_E(foo(), MyException)
{
ASSERT_STREQ("Cucumber overflow", e.msg());
}
Caveats:
- As the macro defines a variable in the current scope, so it can only be used once.
- 因为宏在当前作用域中定义了一个变量,所以只能使用一次。
- C++11 is needed for
std::exception_ptr
- std: exception_ptr需要使用c++ 11
#7
0
I use Matthäus Brandl's macro with the following minor modification:
我使用Matthaus Brandl的宏,做了以下小修改:
Put the line
把线
std::exception_ptr _exceptionPtr;
outside (f.e. before) the macro definition as
外部(f.e. before)的宏定义为
static std::exception_ptr _exceptionPtr;
to avoid multiple definition of the symbol _exceptionPtr
.
为了避免符号_exceptionPtr的多重定义。
#8
-1
You can try Boost lightweight test:
你可以试试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();
}
#1
30
I mostly second Lilshieste's answer but would add that you also should verify that the wrong exception type is not thrown:
我主要支持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();
}
#2
14
Jeff Langr describes a good approach in his book, Modern C++ Programming with Test-Driven Development:
Jeff Langr在他的《使用测试驱动开发的现代c++编程》一书中描述了一种很好的方法:
If your [testing] framework does not support a single-line declarative assert that ensures an exception is thrown, you can use the following structure in your test:
如果您的[测试]框架不支持确保抛出异常的单行声明断言,那么您可以在测试中使用以下结构:
TEST(ATweet, RequiresUserNameToStartWithAnAtSign) { string invalidUser("notStartingWith@"); try { Tweet tweet("msg", invalidUser); FAIL(); } catch(const InvalidUserException& expected) {} }
[...] You might also need to use the try-catch structure if you must verify any postconditions after the exception is thrown. For example, you may want to verify the text associated with the thrown exception object.
[…如果在抛出异常后必须验证任何后置条件,您可能还需要使用try-catch结构。例如,您可能想要验证与抛出的异常对象关联的文本。
TEST(ATweet, RequiresUserNameToStartWithAtSign) { string invalidUser("notStartingWith@"); try { Tweet tweet("msg", invalidUser); FAIL(); } catch(const InvalidUserException& expected) { ASSERT_STREQ("notStartingWith@", expected.what()); } }
(p.95)
(p.95)
This is the approach I've used, and have seen in practice elsewhere.
这是我用过的方法,在实践中也见过。
Edit: As has been pointed out by @MikeKinghan, this doesn't quite match the functionality provided by EXPECT_THROW
; the test doesn't fail if the wrong exception is thrown. An additional catch
clause could be added to address this:
编辑:正如@MikeKinghan指出的,这与EXPECT_THROW提供的功能不太匹配;如果抛出错误的异常,测试不会失败。为了解决这个问题,可以增加附加的catch条款:
catch(...) {
FAIL();
}
#3
13
A colleague came up with the solution by just re-throwing the exception.
一个同事提出了解决方案,只是重新抛出了一个例外。
The knack: no need of extra FAIL() statements, just the two EXPECT... calls that test the bits you actually want: the exception as such and its value.
诀窍:不需要额外的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 );
}
#4
0
I recommend defining a new macro based on Mike Kinghan's approach.
我建议基于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's TEST(foo_test,out_of_range)
example would then be
接下来是Mike的测试(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" );
}
which I think ends up being much more readable.
我觉得这样更容易读懂。
#5
0
I like most of the answers. However, since it seems that GoogleTest provides EXPECT_PRED_FORMAT that helps facilitating this, I'd like to add this option to the list of answers:
我喜欢大部分的答案。然而,由于GoogleTest提供了EXPECT_PRED_FORMAT,这有助于实现这一目的,所以我想将这个选项添加到答案列表中:
MyExceptionCreatingClass testObject; // implements TriggerMyException()
EXPECT_PRED_FORMAT2(ExceptionChecker, testObject, "My_Expected_Exception_Text");
where ExceptionChecker is defined as:
其中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();
}
#6
0
As I need to do several of such tests I wrote a macro that basically includes Mike Kinghan's answer but "removes" all the boilerplate code:
当我需要做几个这样的测试时,我写了一个宏,基本上包含了Mike Kinghan的答案,但是“删除”了所有的样板代码:
#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)
Usage:
ASSERT_THROW_KEEP_AS_E(foo(), MyException)
{
ASSERT_STREQ("Cucumber overflow", e.msg());
}
Caveats:
- As the macro defines a variable in the current scope, so it can only be used once.
- 因为宏在当前作用域中定义了一个变量,所以只能使用一次。
- C++11 is needed for
std::exception_ptr
- std: exception_ptr需要使用c++ 11
#7
0
I use Matthäus Brandl's macro with the following minor modification:
我使用Matthaus Brandl的宏,做了以下小修改:
Put the line
把线
std::exception_ptr _exceptionPtr;
outside (f.e. before) the macro definition as
外部(f.e. before)的宏定义为
static std::exception_ptr _exceptionPtr;
to avoid multiple definition of the symbol _exceptionPtr
.
为了避免符号_exceptionPtr的多重定义。
#8
-1
You can try Boost lightweight test:
你可以试试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();
}