QTest是Qt项目中唯一能验证信号、事件、模型数据流的原生测试工具,需继承QObject、用private slots声明测试函数、共用Qt构建环境、配合QSignalSpy等模拟真实运行时行为,并确保QApplication存在。

QTest 不是“加个测试文件就能跑通”的玩具,它是 Qt 项目里唯一能真正验证信号、事件、模型数据流是否按预期工作的原生工具。用错方式,90% 的测试会变成假绿条——编译过、执行完、没报错,但根本没测到关键路径。
QTest 测试类必须继承 QObject 且用 private slots
这是最常翻车的第一步。QTest 不识别 public 或 protected 成员函数作为测试用例,只认 private slots 下的函数。Qt 的 moc 工具靠这个标记生成测试调度代码。
- 写成
void testSignalEmission()在 public 区域 → QTest 完全忽略,不报错也不执行 - 漏掉
Q_OBJECT宏 → 编译通过,但qExec()找不到任何测试槽,输出 “0 tests run” - 函数名不含 test 前缀(如
initData())→ 虽然能被调用,但不属于测试用例,不会计入统计,也不受initTestCase()/cleanupTestCase()管理
正确写法:
class ConfigTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void testConfigPath(); // ✅ 自动识别为测试用例
void testUserData(); // ✅
void cleanupTestCase();
};QTest 必须和被测代码共用同一套 Qt 构建环境
你用 Qt 6.7.2 + C++17 debug 构建业务模块,测试工程却用 Qt 6.5.3 + C++20 release 链接 → 十有八九在 QVERIFY 或 QSignalSpy 处崩溃,错误信息像 QObject: Cannot create children for a parent that is in a different thread 或直接段错误,跟逻辑无关,纯 ABI 不兼容。
- 检查
find_package(Qt6 ...)版本号是否与主项目完全一致 - 确保
set(CMAKE_CXX_STANDARD 17)和主项目相同,尤其注意CMAKE_CXX_STANDARD_REQUIRED ON是否启用 - 测试可执行文件必须链接
Qt6::Test,且被测模块(如QtScrcpyCore)需以PRIVATE方式传入target_link_libraries - Windows 上若被测 DLL 是隐式链接,测试工程也得用相同运行时(/MDd vs /MD)
测信号、事件、GUI 控件不能只靠 QCOMPARE
QTest 的核心价值不在比值,而在模拟真实 Qt 运行时行为。比如验证一个按钮点击后是否触发了 dataChanged(),用纯 C++ 断言毫无意义。
- 用
QSignalSpy捕获信号:QSignalSpy spy(model, &QAbstractItemModel::dataChanged);,然后调用QTest::mouseClick(button, Qt::LeftButton) - 手动推进事件循环:
QCoreApplication::processEvents(),否则信号可能滞留在队列里未分发 - 验证模型数据流时,别只测
rowCount()返回值,要测spy.wait()是否成功、spy.count()是否符合预期、spy.takeFirst()里参数是否正确 - 对
QTimer::singleShot类异步逻辑,必须用QTest::qWait(100)或更稳妥的spy.wait(500),不能依赖 sleep
DLL 内部非导出类怎么测:友元 vs TestAccess 接口
直接 #include 一个没导出符号的类头文件,链接时必报 LNK2019: unresolved external symbol。QTest 不帮你解决封装问题,得你自己选策略。
- 方案一(推荐用于单元级验证):在被测类头文件中加
friend class TestOrderCalculator;,让测试类访问私有成员。仅限内部开发,不污染对外接口 - 方案二(适合插件/DLL 分发场景):在 DLL 中提供静态
TestAccess接口,如static OrderCalculator* createForTest();,返回已构造好的实例,测试代码只依赖该接口头文件 - 绝对避免:把私有函数改成 public 临时测试 → 后续容易忘记改回,破坏封装契约
- 注意 moc:如果友元测试类本身含
Q_OBJECT,它所在的 .cpp 文件必须被 moc 处理,即对应 .pro 文件中要有SOURCES += test_ordercalculator.cpp,且不能遗漏QT += testlib
最易被忽略的是:所有涉及信号、模型、线程迁移的测试,都必须在 QApplication 或 QGuiApplication 实例存在下运行——哪怕你测的是纯计算函数,只要它内部用了 QMetaObject::invokeMethod 或连接了跨线程信号,缺了应用对象就会静默失败。


















