c++ gtest 当且仅当测试失败时在测试结束时打印附加信息

问题描述 投票:0回答:3

我想做这样的事情:

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. 当许多期望失败时,它会多次打印消息。
  5. 需要重新定义宏来进行各种检查
c++ assert googletest assertions expectations
3个回答
6
投票

做自己想做的事情很难,实际上是一个测试代码味道。特别是,这两个测试(1)做得太多,(2)不可读,因为它们没有描述被测单元的功能。

我推荐两本读物:单元测试是第一和书现代 C++ 编程与测试驱动开发

我建议不要尝试调用 4 个函数,每个函数都检查一些内容,然后想知道如何在失败时打印错误消息,而是建议如下:

  • 问自己:“我在这里测试什么?”当您得到答案后,请使用答案为测试命名。如果你找不到答案,则意味着(我怀疑)测试做得太多了。尝试遵循“每个测试一个断言”准则并相应地拆分测试。
  • 本着同样的精神,查看这 4 个函数中的每一个,并尝试为每个函数命名。如果不能,则说明每个函数检查过多。拆分这些功能。
  • 问问自己是否真的需要期望(而不是断言)。通常使用 EXPECT 而不是 ASSERT 的唯一原因是单个测试做得太多。分开吧。

在此过程结束时,您应该发现自己处于这样一种情况:打印有关测试失败的附加信息的目标只需通过以下操作即可实现:

ASSERT_THAT(Response, EQ("something")) << "Request: " << request;

注意:即使比起点更好,我也不认为上面的例子足够好。测试名称应该非常好,具有描述性,以至于您通过打印

request
的值可以获得零信息。

我意识到这是一种哲学答案;另一方面,它直接来自于我尝试编写良好的、可维护的测试的经验。编写好的测试需要像编写好的代码一样小心,并且会得到很多倍的回报:-)


4
投票

一个非意识形态的答案(基于各地的信息):

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 类。


0
投票

如果您使用 TEST_F 并以 ::testing::Test 为基础实现测试类,则可以在 TearDown() 中检查测试是否有任何失败。

class TestsForFunctionX: public ::testing::Test
{
private:
    void TearDown() override
    {
        if (HasFailure())
        {
            std::cerr << "My additional info";
        }
    }
}

TEST_F(TestsForFunctionX, BadInput)
{
    ASSERT_TRUE(false);
}  
© www.soinside.com 2019 - 2024. All rights reserved.