C++ 测试抛出的特定异常类型并且该异常具有正确的属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23270078/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Test a specific exception type is thrown AND the exception has the right properties
提问by Mr. Boy
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.msg() == "Cucumber overflow"
.
How is this best implemented in GTest?
这在 GTest 中如何最好地实现?
采纳答案by Mike Kinghan
I mostly second Lilshieste's answer but would add that you also should verify that the wrongexception 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();
}
回答by minastaros
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 );
}
回答by Lilshieste
Jeff Langr describes a good approach in his book, Modern C++ Programming with Test-Driven Development:
Jeff Langr 在他的书《Modern C++ Programming with Test-Driven Development》中描述了一种很好的方法:
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.
TEST(ATweet, RequiresUserNameToStartWithAtSign) { string invalidUser("notStartingWith@"); try { Tweet tweet("msg", invalidUser); FAIL(); } catch(const InvalidUserException& expected) { ASSERT_STREQ("notStartingWith@", expected.what()); } }
(p.95)
如果您的 [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 页)
This is the approach I've used, and have seen in practice elsewhere.
这是我使用过的方法,并且在其他地方的实践中也看到过。
Edit:As has been pointed out by @MikeKinghan, this doesn't quitematch 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();
}
回答by Matth?us Brandl
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
- 由于宏定义了当前作用域内的变量,所以只能使用一次。
- 需要 C++11
std::exception_ptr
回答by redwizard792
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
迈克的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" );
}
which I think ends up being much more readable.
我认为最终更具可读性。
回答by Max
My version; it produces the same output as EXPECT_THROW and just adds the string test:
我的版本;它产生与 EXPECT_THROW 相同的输出,只是添加了字符串测试:
#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."; \
}
回答by Dave Reikher
Expanding on previous answers, a macro that verifies that an exception of a given type was thrown and the message of which starts with the provided string.
扩展以前的答案,一个宏验证是否抛出了给定类型的异常,并且其消息以提供的字符串开头。
The test fails if either no exception is thrown, the exception type is wrong or if the message doesn't start with the provided string.
如果未抛出异常、异常类型错误或消息不以提供的字符串开头,则测试失败。
#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";\
}
Usage example:
用法示例:
ASSERT_THROWS_STARTS_WITH(foo(-2), std::invalid_argument, "Bad argument: -2");
回答by morini
I use Matth?us Brandl's macro with the following minor modification:
我使用 Matth?us Brandl 的宏并进行了以下小的修改:
Put the line
把线
std::exception_ptr _exceptionPtr;
outside (f.e. before) the macro definition as
外部(fe之前)宏定义为
static std::exception_ptr _exceptionPtr;
to avoid multiple definition of the symbol _exceptionPtr
.
避免符号的多重定义_exceptionPtr
。
回答by EdS
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();
}
回答by M310
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();
}