最小测试用例
#include <QTest>
class TestMath : public QObject {
Q_OBJECT
private slots:
void testAddition() {
QCOMPARE(1 + 1, 2);
}
void testDivision() {
QVERIFY(10 / 2 == 5);
QVERIFY2(10 / 3 == 3, "整数除法应截断"); // 带消息
}
};
QTEST_MAIN(TestMath) // 自动生成 main
常用断言
QVERIFY(condition); // 真
QVERIFY2(condition, "message"); // 真 + 消息
QCOMPARE(actual, expected); // 相等(自动类型安全)
QCOMPARE(str, "hello"); // QString 直接比较
QCOMPARE(list.size(), 3);
QTRY_COMPARE(obj.property(), value); // 重试直到相等(异步测试)
QTRY_VERIFY(condition); // 重试直到 true
QVERIFY_EXCEPTION_THROWN(expr, Exc); // 确认抛异常
QBENCHMARK { doWork(); } // 性能测试
初始化和清理
class TestDatabase : public QObject {
Q_OBJECT
private:
QSqlDatabase *db;
private slots:
// 每个 test 用例前
void init() { db = new QSqlDatabase(...); }
// 每个 test 用例后
void cleanup() { delete db; db = nullptr; }
// 所有用例前(private slot!)
void initTestCase() { /* 建表、加载测试数据 */ }
// 所有用例后
void cleanupTestCase() { /* 清空数据库 */ }
void testInsert() { /* ... */ }
void testQuery() { /* ... */ }
};
数据驱动测试
class TestParser : public QObject {
Q_OBJECT
private slots:
void testHexToInt_data() {
QTest::addColumn<QString>("hex");
QTest::addColumn<int>("expected");
QTest::newRow("zero") << "00" << 0;
QTest::newRow("ten") << "0A" << 10;
QTest::newRow("max") << "FF" << 255;
QTest::newRow("mixed") << "A5" << 165;
}
void testHexToInt() {
QFETCH(QString, hex);
QFETCH(int, expected);
QCOMPARE(hexToInt(hex), expected);
}
};
// 4 行测试数据 → 自动生成 4 个独立测试用例
GUI 测试
class TestMainWindow : public QObject {
Q_OBJECT
private slots:
void testButtonClick() {
MainWindow w;
w.show();
// 查找控件
auto *btn = w.findChild<QPushButton *>("submitBtn");
QVERIFY(btn != nullptr);
// 模拟点击
QTest::mouseClick(btn, Qt::LeftButton);
// 验证结果
auto *label = w.findChild<QLabel *>("resultLabel");
QCOMPARE(label->text(), QString("提交成功"));
}
void testKeyInput() {
QLineEdit edit;
QTest::keyClicks(&edit, "hello");
QCOMPARE(edit.text(), QString("hello"));
QTest::keyClick(&edit, Qt::Key_Return);
// 测试回车事件
}
};
信号测试
void testSignalEmission() {
Worker worker;
QSignalSpy spy(&worker, &Worker::finished);
QVERIFY(spy.isValid());
worker.start();
QVERIFY(spy.wait(1000)); // 等 1 秒
QCOMPARE(spy.count(), 1); // 信号发射 1 次
// 检查信号参数
QList<QVariant> args = spy.takeFirst();
QCOMPARE(args.at(0).toInt(), 42); // 第一个参数
}
项目组织
tests/
├── CMakeLists.txt
├── tst_math.cpp # 纯逻辑测试
├── tst_protocol.cpp # 协议解析测试
├── tst_database.cpp # DB 集成测试
├── tst_mainwindow.cpp # GUI 测试
└── testdata/
├── frames.bin # 测试用二进制数据
└── fixtures.sql # 数据库初始数据
# tests/CMakeLists.txt
find_package(Qt6 REQUIRED COMPONENTS Test)
function(add_qt_test name)
add_executable(${name} ${name}.cpp)
target_link_libraries(${name} PRIVATE Qt6::Test myapp_lib)
add_test(NAME ${name} COMMAND ${name})
endfunction()
add_qt_test(tst_math)
add_qt_test(tst_protocol)
add_qt_test(tst_database)
# 运行: ctest --output-on-failure
常见模式
// 测试私有成员(加 friend)
class MyClass {
friend class TestMyClass; // 仅测试可以访问
int privateMethod();
};
// 测试异步操作
void testAsync() {
Worker w;
QSignalSpy spy(&w, &Worker::done);
QMetaObject::invokeMethod(&w, "start", Qt::QueuedConnection);
QVERIFY(spy.wait(3000));
}