第 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
-style) 格式字符串,后面是要代替格式字符串中格式标记的参数。然后,我们将数据集流到新的表格行中。首先是任意字符串,然后是对该字符串应用QString::toUpper() 函数的预期结果。
你可以把测试数据看作一个二维表格。在我们的例子中,它有两列string
和result
以及三行。此外,每一行都有一个名称和一个索引:
索引 | 名称 | 字符串 | 结果 |
---|---|---|---|
0 | 全下 | "你好" | 你好 |
1 | 混合 | "你好" | 你好 |
2 | 全高 | "你好" | HELLO |
当数据流进入行时,每个数据都会被断言为与其提供值的列的类型相匹配。如果任何断言失败,测试就会中止。
在给定的测试函数的数据表中,行和列的名称应是唯一的:如果两个行共享一个名称,或两个列共享一个名称,(自 Qt Test 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-cmake
中的qt-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 *********
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.