项目开发中QTest单元测试的使用

目录

一、项目使用背景

二、测试常用的宏

    2.1 断言相关宏

        2.2 数据驱动测试宏

   2.3 GUI用户交互相关宏

  2.4 性能测试相关宏

三、一个简单使用实例


一、项目使用背景


        虽然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)

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值