目录
一、项目使用背景
虽然Google Test功能丰富,社区大,但是在Qt作为UI架构的项目中,程序深度集成 Qt,同时对GUI测试要求更高,此时QTest更方便高效,同时其具有轻量化,宏驱动和支持数据驱动测试等特点;在停车管控项目中有很多模块在软件迭代中功能相当重要但繁琐,如计费、过滤等等,既要测试各种收费规则的准确性,又要保证其计费效率等,此时使用QTest模块进行单元测试很有必要。
gtest先关参考:https://blog.youkuaiyun.com/WSTONECH/article/details/145016691
二、测试常用的宏
2.1 断言相关宏
QVERIFY(condition); // 条件为真则通过,否则失败
QVERIFY2(condition, message); // 同QVERIFY,失败时输出自定义消息QTEST_ASSERT(condition); // 同QVERIFY,但不会被编译器优化掉
QTEST_ASSERT2(condition, msg);QCOMPARE(actual, expected); // 比较两个值是否相等,输出详细差异
QCOMPARE(actual, 5); // 若不等,自动显示 actual 的实际值QEXPECT_FAIL("", "", Abort); // 预期失败(用于尚未修复的bug)
2.2 数据驱动测试宏
QTest::addColumn() 添加列(变量名)
QTest::newRow() 添加行(变量值)
QTest::QFETCH () 取值
void myTestFunction_data() //添加数据函数xxx_data()
{
QTest::addColumn<int>("input");
QTest::addColumn<int>("expected");QTest::newRow("zero") << 0 << 0;
QTest::newRow("positive") << 5 << 25;
QTest::newRow("negative") << -3 << 9;
}
void myTestFunction() {
QFETCH(int, input);
QFETCH(int, expected);
QCOMPARE(input * input, expected);
}
2.3 GUI用户交互相关宏
QTest::keyClick(widget, Qt::Key_A); // 模拟按下A键
QTest::keyPress(widget, Qt::Key_Enter); // 按下回车键
QTest::keyRelease(widget, Qt::Key_Enter); // 释放回车键QTest::mouseClick(button, Qt::LeftButton); // 模拟鼠标左键点击
QTest::mouseDClick(label, Qt::LeftButton); // 双击
QTest::mouseMove(target, QPoint(10,10)); // 移动鼠标
2.4 性能测试相关宏
QBENCHMARK {
std::sort(list.begin(), list.end());
} // 多次运行取平均值 运行时间,运行时间基本固定,对于运行快的接口自然运行次数多从而尽可能消除统计误差,对于运行时间长的接口,运行次数少,但运行时间长本身统计误差也少
QBENCHMARK_ONCE {
complexOperation();
} // 仅运行一次(适合不可重复操作)
QBENCHMARK_SUSPEND {
setup(); // 不计入性能统计的准备代码
}
三、一个简单使用实例
// 测试类
class TestCalculator : public QObject
{
Q_OBJECT
//测试函数需声明为 private slots 中的槽函数,以便框架通过元对象系统调用,无需信号连接,支持直接反射调用。
//命名以 test 开头是约定俗成的做法,便于识别和运行。
//Qt Test 框架会自动查找与测试函数同名但后缀为 _data 的函数。当运行 testAddition 时,框架会先调用 testAddition_data 来准备数据集。
//之后,testAddition 函数体将为数据集中的每一行执行一次,可以使用 QFETCH 宏来获取当前行的数据。
private slots:
void initTestCase(); // 整个测试前执行一次
void cleanupTestCase(); // 所有测试后执行一次
void cleanup(); // 每个测试后执行
void testAddition_data(); // 数据驱动测试的数据 为testAddition准备数据
void testAddition(); // 实际测试加法
void testSignalEmitted(); // 测试信号是否发出
void testGuiInteraction(); // GUI 交互测试
};
测试类实现代码
#include "TestCalculator.h"
#include<QLineEdit>
#include<QDebug>
#pragma execution_character_set("utf-8")
void TestCalculator::initTestCase()
{
qDebug() << "测试用例全局资源初始化" << endl;
}
void TestCalculator::cleanupTestCase()
{
qDebug() << "测试用例全局资源回收" << endl;
}
void TestCalculator::cleanup()
{
qDebug() << "测试用例资源环境清理" << endl;
}
// 数据驱动测试:提供多组测试数据
void TestCalculator::testAddition_data()
{
QTest::addColumn<int>("a");//用于在数据驱动测试中定义一列整型数据,列名为 "a"
QTest::addColumn<int>("b");
QTest::addColumn<int>("expected");
QTest::newRow("positive") << 2 << 3 << 5;//指定数据行名称 绑定2->a 3->b 5->expected
QTest::newRow("negative") << -1 << -1 << -2;
QTest::newRow("mixed") << 5 << -3 << 2;
QTest::newRow("zero") << 0 << 0 << 0;
}
void TestCalculator::testAddition()
{
QFETCH(int, a);//从当前测试数据行中提取类型为 int、列名为 a 的数据,并将其赋值给变量 a
QFETCH(int, b);
QFETCH(int, expected);
Calculator calc;
calc.add(a, b);
QCOMPARE(calc.add(a, b), expected); // 但 add 是 void,所以改为直接比较行为
// 正确方式:我们应在 Calculator 中增加 getResult() 方法,或者监听信号
}
// 正确方式:通过信号验证结果
void TestCalculator::testSignalEmitted()
{
Calculator calc;
//QSignalSpy用于捕获对象发出的信号及其参数,适用于异步或无返回值的场景。
QSignalSpy spy(&calc, &Calculator::calculationCompleted);
calc.add(3, 4);
// 合理验证信号调用次数 验证信号发出一次
QCOMPARE(spy.count(), 1);
// 获取信号参数 安全获取首个信号的参数列表,并通过索引访问结果值。
QList<QVariant> arguments = spy.takeFirst();
QCOMPARE(arguments.at(0).toInt(), 7);
}
// GUI 测试:模拟用户输入并验证输出
void TestCalculator::testGuiInteraction()
{
QLocaleTest widget;
widget.show();
// 模拟输入
// 通过 objectName 查找 lineEdit
QLineEdit* lineEditA = widget.findChild<QLineEdit*>("inputA");
QVERIFY(lineEditA != nullptr); // 确保找到控件
// 模拟输入
QTest::keyClicks(lineEditA, "10");
QLineEdit* lineEditB = widget.findChild<QLineEdit*>("inputB");
QVERIFY(lineEditB != nullptr); // 确保找到控件
QTest::keyClicks(lineEditB, "20");
// 模拟点击按钮
auto* button = widget.findChild<QPushButton*>("button");
QVERIFY(button != nullptr); // 确保找到控件
QTest::mouseClick(button, Qt::LeftButton);
auto* resultLabel = widget.findChild<QPushButton*>("resultLabel");
QVERIFY(resultLabel != nullptr); // 确保找到控件
// 验证结果显示
QCOMPARE(resultLabel->text(), QString("结果: 30"));
}
// 主函数:运行测试
QTEST_MAIN(TestCalculator)
1536





