qtcanpool 知 09:测试框架

文章目录

  • 前言
  • 不满
  • 改进
  • 优化
  • 后语

前言

很久以前,作者写的代码都没有测试用例,最多就是写个 demo 验证一下,毕竟不是专业出身,也没经过大公司的洗礼。
后来,参与到一些项目才知道有专门的测试,而且开发也要测试(开发自测、白盒测试、单元测试)。有兴趣的读者可以去了解一下 TDD(测试驱动开发)方法论。

关于c/c++的测试,作者先后用到过 cpptest、gtest、CMockery。但是,对于 Qt 这种带界面的程序该怎么测试呢?

Qt Test 是一个基于 Qt 的应用程序和库的单元测试框架。Qt Test 提供了单元测试框架中常见的所有功能,以及用于测试图形用户界面的扩展。
Qt Test 旨在简化基于 Qt 的应用程序和库的单元测试编写。
官方介绍:https://doc.qt.io/qt-6/qtest-overview.html

不满

根据 Qt 官方的测试教程,编写一个最简单的测试用例 tst_qstring.cpp 如下所示:

// tst_qstring.cpp
#include <QtTest/QtTest>// 测试套
class tst_QString: public QObject    
{Q_OBJECTprivate slots:void toUpper();  // 测试用例
};void tst_QString::toUpper()
{QString str = "Hello";QVERIFY(str.toUpper() == "HELLO");
}QTEST_MAIN(tst_QString)#include "tst_qstring.moc"

QTEST_MAIN 宏定义如下,实际上是定义一个 main 函数:

#define QTEST_MAIN(TestObject) \
int main(int argc, char *argv[]) \
{ \QTEST_MAIN_IMPL(TestObject) \
}#define QTEST_MAIN_IMPL(TestObject) \TESTLIB_SELFCOVERAGE_START(#TestObject) \QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \QApplication app(argc, argv); \app.setAttribute(Qt::AA_Use96Dpi, true); \QTEST_DISABLE_KEYPAD_NAVIGATION \TestObject tc; \QTEST_SET_MAIN_SOURCE_PATH \return QTest::qExec(&tc, argc, argv);

作者的不满,不是不满意,而是不满足,具体的不满足是:一个 Qt 测试应用程序只能测试一个测试类,如果需要编写很多测试类,一个个执行测试程序也太麻烦了。

解释如下:一个测试文件中通过 QTEST_MAIN 直接定义了一个 main 函数,通常一个应用程序只能有一个 main 函数,这导致一个测试应用程序只能测试一个测试类,一个测试类通常只测试一个类。

改进

问题描述:一个测试应用程序如何测试多个类?
问题分析:既然 QTEST_MAIN 宏是直接定义一个 main 函数,那么可以参考这个宏,main 函数由用户自己定义,然后在自定义的 main 函数中实例化不同的测试类进行测试,这样就可以在一个 main 函数执行多个测试类,从而实现一个测试应用程序测试多个类。

改进后的代码如下:(由一个 tst_qstring.cpp 文件变成了三个文件:tst_qstring.h、tst_qstring.cpp、main.cpp)

// tst_qstring.h
#pragma once#include <QtTest/QtTest>class tst_QString: public QObject    
{Q_OBJECTprivate slots:void toUpper();  // 测试用例
};// tst_qstring.cpp
#include "tst_qstring.h"void tst_QString::toUpper()
{QString str = "Hello";QVERIFY(str.toUpper() == "HELLO");
}// main.cpp
#include <QtTest/QtTest>
#include <QApplication>// 包含测试套头文件
#include "tst_qstring.h"#define TEST_RUN(TestObject) \TestObject TestObject##_var; \QTest::qExec(&TestObject##_var, argc, argv);int main(int argc, char *argv[])
{QApplication app(argc, argv);// 执行一个测试套TEST_RUN(tst_QString)return 0;
}

使用方法:如果想增加其它测试文件,只需要定义对应的 h 和 cpp 文件,然后在 main.cpp 中包含头文件,在 main 函数中调用 TEST_RUN 进行执行。

思考一下:初看没啥问题,实际使用的时候,太麻烦了。每次要多定义一个 h 文件,还要在 main.cpp 中做那么多操作,糟糕的设计。(这个框架思路有点像 cpptest,声明没有借鉴 cpptest)

优化

作者以前先用的 cpptest,后来用了 gtest 后,很爽,只需要调用一个相关宏来定义测试用例,然后 main 函数中调用宏 RUN_ALL_TESTS 就可以运行所有测试用例。

优化思路:

  • 定义一个 TEST_ADD 宏,用于在 cpp 文件中取代 QTEST_MAIN。QTEST_MAIN 是直接定义 main 函数,而 TEST_ADD 只是将测试类添加到测试队列中。
  • 定义一个 TEST_RUN_ALL 宏,用于在 main 函数中运行所有通过 TEST_ADD 添加的测试用例。

问题的关键点:TEST_ADD 宏该如何实现,才能在 main 函数执行前将测试类添加到测试队列中。因为,main 函数通常是程序的入口函数,可以简单理解为程序运行时执行的第一个函数。

TEST_ADD 宏实现思路:

  • 首先,TEST_ADD 宏被先执行,因为宏是在预处理阶段执行。
  • 如果 TEST_ADD 宏实现为函数调用,会有问题,因为在函数外面调用一个函数会报编译错误。
  • 在某些特定的编译器中,有 constructor 和 destructor 属性,可以用来指定某些函数在程序启动时执行(相当于“构造函数”)或在程序退出时执行(相当于“析构函数”)。这并不是标准C语言的特性,但在某些编译器下,确实可以用来模拟类似构造函数的效果。简单理解,构造函数可以先于 main 函数执行。但是这特性也不适合 TEST_ADD 宏。
  • 利用静态变量,main 函数是在所有静态变量和全局变量初始化完成后执行。(参考了 gtest,但没完全看明白 gtest 的实现)

测试框架实现的部分代码如下:(详细代码:https://gitee.com/icanpool/qtcanpool/tree/master/tests/common)

///
// tst_global.h/* UnitTest */
class UnitTest
{
public:UnitTest();~UnitTest();
public:static UnitTest &instance();void add(QObject *obj);void run(int argc, char *argv[]);
private:QVector<QObject *> s_testObjects;
};/* TestInfo */
class TestInfo
{
public:TestInfo(QObject *obj);
};#define TEST_ADD(TestObject)                                             \class TestObject##_TestClass                                         \{                                                                    \public:                                                              \static TestInfo _info;                                           \};                                                                   \TestInfo TestObject##_TestClass::_info = TestInfo(new TestObject);#define TEST_RUN_ALL() UnitTest::instance().run(argc, argv);///
// tst_global.cpp
UnitTest &UnitTest::instance()
{static UnitTest s_testInstance;return s_testInstance;
}void UnitTest::add(QObject *obj)
{if (obj) {s_testObjects.append(obj);}
}void UnitTest::run(int argc, char *argv[])
{for (QObject *obj : s_testObjects) {QTest::qExec(obj, argc, argv);}qDeleteAll(s_testObjects);s_testObjects.clear();
}TestInfo::TestInfo(QObject *obj)
{UnitTest::instance().add(obj);
}///
// main.cpp
#include <QApplication>#include "tst_global.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);TEST_RUN_ALL();return 0;
}

使用方法:

  • 按 Qt 官方的教程编写测试文件,然后用 TEST_ADD 取代 QTEST_MAIN。
  • 编写 main.cpp 文件,在 main 函数中调用 TEST_RUN_ALL 宏函数。

具体使用,可以参考 qtcanpool 中的测试代码:https://gitee.com/icanpool/qtcanpool/tree/master/tests

后语

经过一段时间的测试,作者简单的体会是:Qt 虽然写的是界面程序,但本质是 C++,只要按照类来理解就方便测试了,主要测试类的接口。界面程序相比后台程序多了一些鼠标和按键的测试,这部分 QTest 也提供了相应的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/888543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BioDeepAV:一个多模态基准数据集,包含超过1600个深度伪造视频,用于评估深度伪造检测器在面对未知生成器时的性能。

2024-11-29, 由罗马尼亚布加勒斯特大学创建BioDeepAV数据集&#xff0c;它专门设计来评估最先进的深度伪造检测器在面对未见过的深度伪造生成器时的泛化能力&#xff0c;这对于提高检测器的鲁棒性和适应性具有重要意义。 数据集地址&#xff1a;biodeep 一、研究背景&#xff1…

工业—使用Flink处理Kafka中的数据_ChangeRecord1

使用 Flink 消费 Kafka 中 ChangeRecord 主题的数据,当某设备 30 秒状态连续为 “ 预警 ” ,输出预警 信息。当前预警信息输出后,最近30

Flink四大基石之State(状态) 的使用详解

目录 一、有状态计算与无状态计算 &#xff08;一&#xff09;概念差异 &#xff08;二&#xff09;应用场景 二、有状态计算中的状态分类 &#xff08;一&#xff09;托管状态&#xff08;Managed State&#xff09;与原生状态&#xff08;Raw State&#xff09; 两者的…

opencv-android编译遇到的相关问题处理

1、opencv-android sdk下载 下载地址&#xff1a;https://opencv.org/releases/ 下载安卓SDK即可 2、解压下载好的SDK 3、导入opencv的SDK到安卓项目中 导入步骤在/OpenCV-android-sdk/sdk/build.gradle文件的注释中写的非常详细&#xff0c;大家可安装官方给出的步骤导入。…

OpenSSH-9.9p1 OpenSSL-3.4.0 升级步骤详细

前言 收到漏洞扫描通知 OpenSSH 安全漏洞(CVE-2023-38408) OpenSSH 安全漏洞(CVE-2023-51385) OpenSSH 安全漏洞(CVE-2023-51384) OpenSSH 安全漏洞(CVE-2023-51767) OpenSSH 安全漏洞(CVE-2023-48795) OpenSSH&#xff08;OpenBSD SecureShell&#xff09;是加拿大OpenBSD计划…

Python毕业设计选题:基于Flask的医疗预约与诊断系统

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 系统首页 疾病信息 就诊信息 个人中心 管理员登录界面 管理员功能界面 用户界面 医生…

sql删除冗余数据

工作或面试中经常能遇见一种场景题&#xff1a;删除冗余的数据&#xff0c;以下是举例介绍相应的解决办法。 举例&#xff1a; 表结构&#xff1a; 解法1&#xff1a;子查询 获取相同数据中id更小的数据项&#xff0c;再将id不属于其中的数据删除。-- 注意&#xff1a;mysql中…

数据链路层(四)---PPP协议的工作状态

1 PPP链路的初始化 通过前面几章的学习&#xff0c;我们学了了PPP协议帧的格式以及组成&#xff0c;那么对于使用PPP协议的链路是怎么初始化的呢&#xff1f; 当用户拨号上网接入到ISP后&#xff0c;就建立起了一条个人用户到ISP的物理链路。这时&#xff0c;用户向ISP发送一…

基于“微店 Park”模式下 2+1 链动模式商城小程序的创新发展与应用研究

摘要&#xff1a;本文以“微店 Park”从“开店工具”向“众创平台”的转型为背景&#xff0c;深入探讨 21 链动模式商城小程序在该平台情境下的应用潜力与创新发展路径。通过剖析“微店 Park”的运营模式&#xff0c;包括灵活承租、低成本入驻、多元流量引流等特点&#xff0c;…

《船舶物资与市场》是什么级别的期刊?是正规期刊吗?能评职称吗?

问题解答 问&#xff1a;《船舶物资与市场》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《船舶物资与市场》级别&#xff1f; 答&#xff1a;国家级。主管单位&#xff1a;中国船舶集团有限公司 主办单…

2024年认证杯SPSSPRO杯数学建模B题(第一阶段)神经外科手术的定位与导航解题全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 B题 神经外科手术的定位与导航 原题再现&#xff1a; 人的大脑结构非常复杂&#xff0c;内部交织密布着神经和血管&#xff0c;所以在大脑内做手术具有非常高的精细和复杂程度。例如神经外科的肿瘤切除手术或血肿清除手术&#xff0c;通常需要…

ElementUI 问题清单

1、form 下面只有一个 input 时回车键刷新页面 原因是触发了表单默认的提交行为&#xff0c;给el-form 加上submit.native.prevent就行了。 <el-form inline submit.native.prevent><el-form-item label"订单号"><el-inputv-model"query.order…

vulnhub靶场之momentum-2

前言 靶机采用virtual box虚拟机&#xff0c;桥接网卡 攻击采用VMware虚拟机&#xff0c;桥接网卡 靶机&#xff1a;momentum-2 192.168.1.40 攻击&#xff1a;kali 192.168.1.16 主机发现 使用arp-scan -l扫描 信息收集 使用namp扫描 这里的命令对目标进行vulner中的漏…

Python语法基础---正则表达式

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 我们这个文章所讲述的&#xff0c;也是数据分析的基础文章&#xff0c;正则表达式 首先&#xff0c;我们在开始之前&#xff0c;引出一个问题。也是我们接下来想要解决的问题。…

小家电出海,沃丰科技助力保障售后服务的及时性与高效性

随着全球化步伐的加快&#xff0c;小家电行业也逐渐迈向国际市场&#xff0c;面向全球消费者提供服务。然而&#xff0c;跨国界的销售和服务挑战也随之而来&#xff0c;尤其是售后服务的及时性与高效性成为了企业亟需解决的问题。沃丰科技凭借其全渠道在线客服、工单系统和视频…

Vulnhub靶场 Matrix-Breakout: 2 Morpheus 练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集0x03 漏洞查找与利用1. 文件上传2. 提权 0x04 总结 0x00 准备 下载连接&#xff1a;https://download.vulnhub.com/matrix-breakout/matrix-breakout-2-morpheus.ova 介绍&#xff1a; This is the second in the Matrix-Br…

美畅物联丨智能监控,高效运维:视频汇聚平台在储能领域的实践探索

在当今全球能源格局不断变化的大背景下&#xff0c;对清洁能源的需求正以惊人的速度增长。储能项目作为平衡能源供需、提升能源利用效率的关键环节&#xff0c;其规模和复杂度也在不断攀升。在储能项目的运营管理过程中&#xff0c;安全监控、设备运维以及数据管理等方面面临着…

提升用户体验、创新产品与高效运营,企业发展三驾马车

​在当今竞争激烈的市场环境中&#xff0c;企业要想脱颖而出并持续发展&#xff0c;需同时在提升用户体验、推动产品创新以及实现内部高效运营方面下功夫。 提升用户体验至关重要。它能提高用户满意度和忠诚度&#xff0c;增加用户口碑与推荐&#xff0c;提升企业品牌形象。可通…

基于ZooKeeper搭建Hadoop高可用集群

ZooKeeper搭建Hadoop高可用集群 在之前安装的Hadoop3.3.6集群中HDFS NameNode 和 YARN ResourceManager 都是单节点&#xff0c;集群不具有高可用性。 HDFS 高可用架构 HDFS 高可用架构主要组件&#xff1a; Active NameNode 和 Standby NameNode&#xff1a; 两台 NameNode…

机器学习—学习过程

给定训练集构建决策树的过程有几个步骤。 给出了一组由十个猫和狗的例子组成的训练集&#xff0c;决策树学习的第一步是我们必须决定在根节点使用什么特性&#xff0c;这是决策树顶部的第一个节点&#xff0c;通过一个算法&#xff0c;假设我们决定选择根节点中的特性&#xf…