当且仅当测试失败时,c ++ gtest在测试结束时打印附加信息
c++ gtest print additional information in the end of a test when and only when the test fails
我想做这样的事情:
TEST(MyTestFixture, printAfterExpectationFailure)
{
const string request("bring me tea");
const string&& response = sendRequestAndGetResponse(request);
checkResponseWithExpectarions1(response);
checkResponseWithExpectarions2(response);
checkResponseWithExpectarions3(response);
checkResponseWithExpectarions4(response);
if (anyOfExpectsFailed())
cout << "Request: " << request << "\nresponse: " << response << endl;
}
TEST(MyTestFixture, printAfterAssertionFailure)
{
const string request("bring me tea");
const string&& response = sendRequestAndGetResponse(request);
doWhenFailure([&request, &response]()
{
cout << "Request: " << request << "\nresponse: " << response << endl;
});
checkResponseWithAssertion1(response);
checkResponseWithAssertion2(response);
checkResponseWithAssertion3(response);
checkResponseWithAssertion4(response);
}
我想在且仅当 expectations/assertions 失败时打印一些附加信息。
我知道我可以做这样的事情:
#define MY_ASSERT_EQ(lhr, rhs, message) if(lhs != rhs) ASSERT_EQ(lhs, rhs) << message
但这种解决方案并不舒服,因为:
- 我检查了两次
- 我使用预处理器,所以可能需要一些时间才能找到
错误。
- 当函数真正嵌套时,该解决方案很难使用。
- 当许多 EXPECTations 失败时,它会多次打印消息。
- 需要重新定义宏以进行各种检查
做自己想做的事情很难,其实也是一种考验code smell。特别是,这两个测试 (1) 做的太多,(2) 不可读,因为它们没有描述被测单元的功能。
我推荐两个读物:Unit Tests are FIRST and the book Modern C++ Programming with Test-Driven Development。
与其尝试调用 4 个函数,每个函数都检查一些东西,然后想知道在失败的情况下如何打印错误消息,我建议如下:
- 问问自己:"what am I testing here?"当你有答案时,使用答案给测试命名。如果你找不到答案,这意味着(我怀疑)测试做的太多了。尝试遵循 "one assert per test" 指南并相应地拆分测试。
- 本着同样的精神,看看这 4 个函数中的每一个,并尝试给每个函数起一个名字。如果不能,则每个函数都在检查太多。拆分这些功能。
- 问问自己是否真的需要期望(而不是断言)。通常使用 EXPECT 而不是 ASSERT 的唯一原因是因为单个测试做的太多了。拆分它。
在此过程结束时,您应该会发现自己处于打印有关测试失败的附加信息的目标,只需使用类似以下内容即可实现:
ASSERT_THAT(Response, EQ("something")) << "Request: " << request;
注意:如果起点更好,我认为上面的例子还不够好。测试名称应该如此好,如此具有描述性,以至于通过打印 request
.
的值您将获得零信息
我意识到这是一种哲学上的回答;另一方面,它直接来自于我尝试编写良好的、可维护的测试的经验。编写好的测试需要与编写好的代码一样的谨慎,并且会得到很多回报:-)
一个非意识形态的答案(基于来自各地的信息):
QDebugTest.h
class QDebugTest : public ::testing::Test
{
public:
void SetUp() override;
void TearDown() override;
};
QDebugTest.cpp
static std::ostringstream qdebugString;
static void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
switch (type) {
case QtDebugMsg: qdebugString << "qDebug"; break;
case QtInfoMsg: qdebugString << "qInfo"; break;
case QtWarningMsg: qdebugString << "qWarning"; break;
case QtCriticalMsg: qdebugString << "qCritical"; break;
case QtFatalMsg: qdebugString << "qFatal"; break;
}
if (context.file) {
qdebugString << " (" << context.file << ":" << context.line ;
}
if (context.function) {
qdebugString << " " << context.function;
}
if(context.file || context.function) {
qdebugString << ")";
}
qdebugString << ": ";
qdebugString << msg.toLocal8Bit().constData();
qdebugString << "\n";
}
void QDebugTest::SetUp()
{
assert(qdebugString.str().empty());
qInstallMessageHandler(myMessageOutput);
}
void QDebugTest::TearDown()
{
qInstallMessageHandler(0);
if(!qdebugString.str().empty()) {
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
if (test_info->result()->Failed()) {
std::cout << std::endl << qdebugString.str();
}
qdebugString.clear();
}
}
现在从 QDebugTest
而不是 ::testing::Test
导出你的 Fixture-class。
我想做这样的事情:
TEST(MyTestFixture, printAfterExpectationFailure)
{
const string request("bring me tea");
const string&& response = sendRequestAndGetResponse(request);
checkResponseWithExpectarions1(response);
checkResponseWithExpectarions2(response);
checkResponseWithExpectarions3(response);
checkResponseWithExpectarions4(response);
if (anyOfExpectsFailed())
cout << "Request: " << request << "\nresponse: " << response << endl;
}
TEST(MyTestFixture, printAfterAssertionFailure)
{
const string request("bring me tea");
const string&& response = sendRequestAndGetResponse(request);
doWhenFailure([&request, &response]()
{
cout << "Request: " << request << "\nresponse: " << response << endl;
});
checkResponseWithAssertion1(response);
checkResponseWithAssertion2(response);
checkResponseWithAssertion3(response);
checkResponseWithAssertion4(response);
}
我想在且仅当 expectations/assertions 失败时打印一些附加信息。
我知道我可以做这样的事情:
#define MY_ASSERT_EQ(lhr, rhs, message) if(lhs != rhs) ASSERT_EQ(lhs, rhs) << message
但这种解决方案并不舒服,因为:
- 我检查了两次
- 我使用预处理器,所以可能需要一些时间才能找到 错误。
- 当函数真正嵌套时,该解决方案很难使用。
- 当许多 EXPECTations 失败时,它会多次打印消息。
- 需要重新定义宏以进行各种检查
做自己想做的事情很难,其实也是一种考验code smell。特别是,这两个测试 (1) 做的太多,(2) 不可读,因为它们没有描述被测单元的功能。
我推荐两个读物:Unit Tests are FIRST and the book Modern C++ Programming with Test-Driven Development。
与其尝试调用 4 个函数,每个函数都检查一些东西,然后想知道在失败的情况下如何打印错误消息,我建议如下:
- 问问自己:"what am I testing here?"当你有答案时,使用答案给测试命名。如果你找不到答案,这意味着(我怀疑)测试做的太多了。尝试遵循 "one assert per test" 指南并相应地拆分测试。
- 本着同样的精神,看看这 4 个函数中的每一个,并尝试给每个函数起一个名字。如果不能,则每个函数都在检查太多。拆分这些功能。
- 问问自己是否真的需要期望(而不是断言)。通常使用 EXPECT 而不是 ASSERT 的唯一原因是因为单个测试做的太多了。拆分它。
在此过程结束时,您应该会发现自己处于打印有关测试失败的附加信息的目标,只需使用类似以下内容即可实现:
ASSERT_THAT(Response, EQ("something")) << "Request: " << request;
注意:如果起点更好,我认为上面的例子还不够好。测试名称应该如此好,如此具有描述性,以至于通过打印 request
.
我意识到这是一种哲学上的回答;另一方面,它直接来自于我尝试编写良好的、可维护的测试的经验。编写好的测试需要与编写好的代码一样的谨慎,并且会得到很多回报:-)
一个非意识形态的答案(基于来自各地的信息):
QDebugTest.h
class QDebugTest : public ::testing::Test
{
public:
void SetUp() override;
void TearDown() override;
};
QDebugTest.cpp
static std::ostringstream qdebugString;
static void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
switch (type) {
case QtDebugMsg: qdebugString << "qDebug"; break;
case QtInfoMsg: qdebugString << "qInfo"; break;
case QtWarningMsg: qdebugString << "qWarning"; break;
case QtCriticalMsg: qdebugString << "qCritical"; break;
case QtFatalMsg: qdebugString << "qFatal"; break;
}
if (context.file) {
qdebugString << " (" << context.file << ":" << context.line ;
}
if (context.function) {
qdebugString << " " << context.function;
}
if(context.file || context.function) {
qdebugString << ")";
}
qdebugString << ": ";
qdebugString << msg.toLocal8Bit().constData();
qdebugString << "\n";
}
void QDebugTest::SetUp()
{
assert(qdebugString.str().empty());
qInstallMessageHandler(myMessageOutput);
}
void QDebugTest::TearDown()
{
qInstallMessageHandler(0);
if(!qdebugString.str().empty()) {
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
if (test_info->result()->Failed()) {
std::cout << std::endl << qdebugString.str();
}
qdebugString.clear();
}
}
现在从 QDebugTest
而不是 ::testing::Test
导出你的 Fixture-class。