第2章:数据驱动测试

本章演示了如何通过不同的测试数据重复执行测试。

到目前为止,我们将要测试的数据硬编码到测试函数中。如果我们添加更多测试数据,函数可能看起来像这样

QCOMPARE(QString("hello").toUpper(), QString("HELLO"));
QCOMPARE(QString("Hello").toUpper(), QString("HELLO"));
QCOMPARE(QString("HellO").toUpper(), QString("HELLO"));
QCOMPARE(QString("HELLO").toUpper(), QString("HELLO"));

为了防止函数因代码重复而变得杂乱,Qt Test 支持将测试数据添加到测试函数中。我们只需要向我们的测试类添加另一个私有槽。

class TestQString: public QObject
{
    Q_OBJECT

private slots:
    void toUpper_data();
    void toUpper();
};

编写数据函数

测试函数关联的数据函数在其名称末尾附加 _data。我们的数据函数看起来像这样

void TestQString::toUpper_data()
{
    QTest::addColumn<QString>("string");
    QTest::addColumn<QString>("result");

    QTest::newRow("all-lower") << "hello" << "HELLO";
    QTest::newRow("mixed")     << "Hello" << "HELLO";
    QTest::newRow("all-upper") << "HELLO" << "HELLO";
}

首先,我们使用 QTest::addColumn() 函数定义测试表的两个元素:一个测试字符串以及将该字符串应用 QString::toUpper() 函数后的预期结果。

然后,我们使用 QTest::newRow() 函数向表格中添加一些数据。如果需要格式化行名称,例如在迭代生成多行数据时,我们还可以使用 QTest::addRow()。每行数据将成为测试表中的单独一行。

QTest::newRow() 传递一个参数:一个将与数据集相关联的名称,并在测试日志中用于标识数据行。QTest::addRow() 传递一个(printf 強制风格)格式化字符串,后跟用于替换格式化字符串中格式化标记的参数。然后,我们将数据集流式传输到新的表格行中。首先是一个任意的字符串,然后是该字符串应用 QString::toUpper() 函数后的预期结果。

您可以将测试数据视为一个二维表格。在我们的例子中,它有两个名为 stringresult 的列和三行。此外,每行都与一个名称和索引相关联

索引名称字符串结果
0全部小写"hello"HELLO
1混合"Hello"HELLO
2全部大写"HELLO"HELLO

数据流式传输到行中时,每个数据都会被断言为与提供其值的列的数据类型相匹配。如果任何断言失败,则测试被中止。

在给定测试函数的数据表中,行和列的名称应该是唯一的:如果两行共享一个名称,或者两列共享一个名称,那么(自 Qt 6.5 起)将生成一个警告。有关如何将警告视为错误的信息,请参阅 qWarning(),有关如何清除其他警告的信息,请参阅 Test for Warnings

重写测试函数

现在我们可以重写测试函数

void TestQString::toUpper()
{
    QFETCH(QString, string);
    QFETCH(QString, result);

    QCOMPARE(string.toUpper(), result);
}

TestQString::toUpper() 函数将被执行三次,一次对应于我们在关联的 TestQString::toUpper_data() 函数中创建的测试表中的每个条目。

首先,我们使用 QFETCH() 宏获取数据集的两个元素。QFETCH() 函数接受两个参数:元素的类型和元素名称。然后,我们使用 QCOMPARE() 宏执行测试。

这种方法使我们能够很容易地在不修改测试本身的情况下向测试中添加新数据。

准备独立的可执行文件

同样地,要使我们的测试用例成为一个独立可执行文件,需要以下两行代码

QTEST_MAIN(TestQString)
#include "testqstring.moc"

和之前一样,QTEST_MAIN() 宏扩展为一个简单的主方法 main(),它将运行所有的测试函数;由于我们的测试类声明和实现都在一个 .cpp 文件中,我们还需要包含生成的 moc 文件,以便使 Qt 的反射工作。

构建可执行文件

您可以使用 CMake 或 qmake 构建测试用例的可执行文件。

使用 CMake 构建

在您的 CMakeLists.txt 文件中配置您的构建设置

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

cmake_minimum_required(VERSION 3.16)
project(tutorial2 LANGUAGES CXX)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Test Widgets)

qt_standard_project_setup()

qt_add_executable(tutorial2
    testqstring.cpp
)

set_target_properties(tutorial2 PROPERTIES
    WIN32_EXECUTABLE TRUE
    MACOSX_BUNDLE TRUE
)

target_link_libraries(tutorial2 PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Test
    Qt6::Widgets
)

install(TARGETS tutorial2
    BUNDLE  DESTINATION .
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

qt_generate_deploy_app_script(
    TARGET tutorial2
    OUTPUT_SCRIPT deploy_script
    NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})

接下来,从命令行运行 cmake 或使用位于 Qt-prefix/<version>/<platform>/bin/qt-cmakeqt-cmake 方便脚本

<Qt-prefix>/<version>/<platform>/bin/qt-cmake <source-dir> <build-dir> -G Ninja

然后,运行您首选的生成工具来构建可执行文件。在这个例子中,我们使用 Ninja。

ninja

使用 qmake 构建

在您的 .pro 文件中配置您的构建设置

QT += widgets testlib

SOURCES = testqstring.cpp

# install
target.path = $$[QT_INSTALL_EXAMPLES]/qtestlib/tutorial2
INSTALLS += target

接下来,运行 qmake,最后运行 make 来构建您的可执行文件

qmake
make

运行可执行文件

运行生成的可执行文件应给出以下输出

********* Start testing of TestQString *********
Config: Using QtTest library %VERSION%, Qt %VERSION%
PASS   : TestQString::initTestCase()
PASS   : TestQString::toUpper(all-lower)
PASS   : TestQString::toUpper(mixed)
PASS   : TestQString::toUpper(all-upper)
PASS   : TestQString::cleanupTestCase()
Totals: 5 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms
********* Finished testing of TestQString *********

© 2024 Qt 公司 Ltd. 本文档中的文档贡献者是各自的版权所有者。本提供在此的文档是在自由软件基金会(Free Software Foundation)发布的 GNU 自由文档许可协议版本 1.3 条件下许可的。Qt 及相关商标是芬兰以及/或其他国家的 Qt 公司有限公司的商标。所有其他商标均为其各自所有者的财产。