C++ GTest:测试失败时仅在测试结束时打印额外信息。

7
我希望能做类似于以下的事情:
```

我想实现这样的效果:

```
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);
}

我希望在期望和断言失败时打印一些额外的信息。
我知道可以这样做:
#define MY_ASSERT_EQ(lhr, rhs, message) if(lhs != rhs) ASSERT_EQ(lhs, rhs) << message

但这种解决方案并不舒适,因为:

  1. 我需要检查两次
  2. 我使用预处理器,所以找到错误可能需要一些时间。
  3. 当函数真正嵌套时,这个解决方案很难使用。
  4. 当许多EXPECTations失败时,它会多次打印消息。
  5. 必须重新定义所有类型的检查宏。
2个回答

5
做你想做的事情往往很困难,实际上是一种代码异味测试。特别是这两个测试:(1) 做太多了;(2) 不可读,即不描述被测试单元的功能。
我推荐两篇文章:Unit Tests are FIRST和书籍Modern C++ Programming with Test-Driven Development
与其尝试调用 4 个函数,每个函数都检查某些内容,然后想知道如何在失败时打印错误消息,我建议你这样做:
- 首先问自己:“我在这里测试什么?”当你有答案后,使用该答案为测试命名。如果找不到答案,则意味着(我怀疑)测试做得太多了。尽量遵循“一个测试一个断言”的准则,并相应地拆分测试。 - 同样地,查看这 4 个函数中的每一个,尝试为每个函数命名。如果无法命名,则每个函数都检查得太多了。请将这些函数拆分。 - 问自己是否真的需要期望(而不是断言)。通常使用 EXPECT 而不是 ASSERT 的唯一原因是单个测试做得太多了。请将其拆分。
在此过程结束时,你应该能够像这样简单地达到打印测试失败信息的目标:
ASSERT_THAT(Response, EQ("something")) << "Request: " << request;

注意:即使比起起点更好,我也不认为上面的示例足够好。测试名称应该是如此好,如此描述性,以至于打印request的值获得零信息。
我意识到这是一种哲学性的回答;另一方面,它来自于我试图编写良好可维护测试的经验。编写良好测试需要像编写良好代码一样的小心,而且它将会多次付出回报 :-)

同意。一个测试,一个断言。否则,测试与结果之间存在一对多的映射,这使得测试变得无用和模糊不清。 - cowboydan

3
一个非意识形态的答案(基于来自各处的信息):
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();
    }
}

现在将你的 Fixture 类从 ::testing::Test 更改为 QDebugTest

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接