一个字符串处理函数,手动调用时结果正确,放进测试却失败;信号已经发出,测试记录里仍然是0次。这些现象未必都来自业务代码,也可能与测试数据、对象状态或检查时机有关。Qt Test可以验证返回值、对象状态和信号参数,但用例需要先明确“什么条件下,应该得到什么结果”。下面结合测试工程的创建和常见失败情况,介绍具体做法。
一、Qt怎么编写单元测试
Qt Test是Qt提供的测试框架,可以生成独立的测试程序。以下以Qt Creator英文界面为例,已有工程也可以单独添加测试目标。
1、创建测试工程
①点击【File】→【New Project】→【Test Project】,选择【Qt Test Project】→【Choose】。
②在【Test case name】中填写测试类名称,并在【Build system】中选择构建系统。
③根据测试对象决定是否勾选【Requires QApplication】;涉及QWidget控件时,需要相应应用环境。
④需要统一准备和清理数据时,勾选【Generate initialization and cleanup code】,完成创建。
测试目标还需要编译或链接待测业务代码。仅有向导生成的测试类,并不会自动把另一个应用工程中的实现包含进来。
2、编写第一个测试函数
①在继承QObject的测试类中保留Q_OBJECT,将测试函数声明为private slots中的无参void函数。
②在函数中准备输入,调用待测接口,再使用QCOMPARE比较实际值与预期值;判断条件是否成立时使用QVERIFY。
③检查测试目标已链接Qt Test模块,并保留正确的测试入口和元对象构建配置。
例如,验证字符串转大写,可以准备“Hello”,调用转换接口后与“HELLO”比较。预期值应来自接口约定,不宜再调用同一套转换逻辑生成,否则实现中的错误可能被一起带入预期结果。
3、同一规则用不同数据验证
空字符串、普通文本、边界长度等情况,可以放进数据表,不必复制多份测试函数。
①为测试函数增加同名且带_data后缀的数据函数。
②使用QTest::addColumn定义输入和预期结果列,通过QTest::newRow添加有辨识度的数据行。
③在测试函数中用QFETCH读取数据,执行同一套调用和比较。
4、运行并查看结果
①在【Tests】视图中选择用例,通过【Test Results】中的【Run Selected Tests】运行。
②查看失败记录中的函数名、数据行及源码位置,双击对应条目检查代码。
③新增函数没有出现在列表时,在测试树的右键菜单中选择【Rescan Tests】,并检查源文件是否参与构建。
二、Qt测试用例运行结果与预期不一致如何分析
失败以后,先保留实际输出。把断言改成当前返回值,虽然可能让测试通过,却会失去原本要验证的规则。
1、比较值之前,确认两边代表什么
①在【Test Results】中找到失败断言,核对实际值、预期值和数据行。
②检查输入及返回值的类型,留意整数除法、类型转换、字符串空白和大小写。
③核对接口对空输入、无效参数及边界值的约定。
④对浮点结果,结合业务精度检查差值,避免把显示位数相同当作底层数值完全相等。
还要看测试有没有执行到目标分支。例如,文件读取失败后返回空内容,随后发生的字符串比较失败,只是后续表现,原因应从文件打开那一步查起。
2、异步结果不能在调用后立即判断
网络请求、定时器和排队信号都可能在接口返回后才完成。调用结束就检查次数,读到0并不能说明信号没有发送。
①在触发操作前创建QSignalSpy,用QVERIFY检查其isValid结果。
②触发待测操作,通过QTRY_COMPARE或QTRY_VERIFY等待目标条件成立,必要时指定合理超时。
③检查记录到的信号次数与参数,而不只是等待函数是否成功。
④核实测试入口提供了所需应用对象和事件处理环境。无界面但需要事件循环的测试,可以使用QTEST_GUILESS_MAIN。
等待表达式应读取状态,避免把会重复提交任务的调用写进去。单纯延长固定等待时间,也可能只是暂时掩盖执行时序问题。
3、单独通过、整套失败,检查残留状态
①先单独运行失败用例,再运行整套测试,比较失败是否与其他用例有关。
②检查成员变量、静态数据、配置文件、数据库记录及未结束的后台任务。
③在init中建立本次测试需要的状态,在cleanup中清理资源。
initTestCase通常用于整套测试开始前的准备,不能代替每个用例的状态重置。前一项修改了设置、后一项默认设置未变,就可能出现单独运行正常、批量运行失败的情况。
三、Qt测试在本机通过换环境却失败怎么办
本机测试时,文件路径、插件和配置可能恰好都已准备齐全。换到另一台机器,或者由自动化任务启动,这些条件就可能不同。
1、先让运行条件能够对照
①核对Qt版本、编译器、构建配置、工作目录及相关环境变量。
②检查测试数据文件是否随测试部署,避免依赖个人目录中的文件。
③需要定位测试数据时,使用QFINDTESTDATA;需要临时文件时,使用独立临时目录,减少相互覆盖。
如果差异只出现在日期、数字或文本格式上,还应核实区域设置、时区及编码,确认接口约定是否允许这些结果随环境变化。
2、崩溃时查看完整输出
①记录失败时间及用例名称,保留原始日志。
②如果Qt测试崩溃后结果显示不完整,进入【Preferences】→【Testing】→【Qt Test】,取消【Use XML output】,重新运行查看文本输出。
③从【Tests】中运行该用例,并在需要时使用【Debug Test Under Cursor】检查现场。
崩溃不能按普通比较失败处理。先确认对象生命周期、访问位置及调用过程,再判断断言是否需要调整。
总结
单元测试的作用,是让接口约定有可以重复检查的依据。结果不符时,既要看业务实现,也要核对输入、初始状态和完成时机。同步函数、异步信号及依赖外部文件的测试,各有需要留意的条件。找到原因后,把触发问题的数据保留下来,下一次修改就能继续验证。希望这些方法能帮助大家写出便于维护的Qt测试,也能在失败时找到具体线索,减少反复试改。如果还有相关需求,欢迎联系我们交流。