☰
Qt应用测试实战:从测试包拆解到回归体系搭建全路径
2026/10/15 11:13:38 网站建设 项目流程

简介:面向Qt图形界面开发者的实战项目包,聚焦QGraphicsView与QGraphicsScene两大核心模块,演示从自定义图元到鼠标交互的完整实现。功能上支持以鼠标为中心的滚轮缩放、图元拖拽、旋转操作,并可通过render方法将场景渲染成图像完成截取,链路完整。压缩包共82个文件,包含14个cpp源文件与7个头文件,以及编译生成的exe可执行程序、obj中间文件、调试日志和VS工程配置,整体大小14.13MB;文件覆盖源码阅读、编译验证到运行调试的完整环节,适合对照源码理解工程组织。项目代码按图形项、场景、视图分层封装,涉及QGraphicsItem继承重写、事件系统处理以及视图缩放策略等关键知识点,功能模块可复用,便于二次改造。已有662人学习或下载,从工程搭建到交互实现均有据可循,适合正在练习Qt二维图形编程的开发者参考借鉴。

1. 手上一个 QtAppTest220104.zip:先别解压,先想清楚它该是什么

拿到命名为 QtAppTest220104.zip 的压缩包时,大多数人的第一反应是双击解压,第二反应才轮到怀疑。这种命名在 Qt 团队里很常见:QtAppTest 表示内容是某个 Qt 应用的测试相关产物,220104 是归档日期,zip 是跨平台分发格式。它背后通常对应三种场景:一套待评审的测试工程源码、一批已经跑完的测试报告,或者一个用于复现问题的最小工程。这篇文章不猜包里具体装了哪些文件——那份事实只有你本机解开后才能确认——而是把这一类包背后的 Qt 应用测试落地路径讲清楚:拿到包后怎么拆、怎么搭最小测试框架、怎么把用例写厚、以及五个高频雷区为什么炸、怎么绕。适合接手这类包的人、想补 Qt Test 的人,以及准备搭建回归体系的一线开发者。

2. 拆包前先建模:Qt 应用测试工程的标准骨架

很多人拿到 QtAppTest220104.zip 的第一件事是双击解压,我习惯先停一下,想清楚这个包里大概率装了什么再动手。因为在 Qt Test 的世界里,“测试工程”和“测试产物”的组织方式有规律可循。提前建模的好处是:你可以少做几步无效操作,也能更快判断这个包是不是你想要的。

2.1 压缩包里最常出现的四类内容:按文件名辨别用途

在解压之前,先看一下 zip 包内部的顶层条目是必要的。我一般用解压工具的“预览”功能或者命令行unzip -l扫一遍,这能避免把纯报告包当成源码工程来读。这类以日期命名的测试包,内容通常逃不出下面四类:

类别常见文件用途收到后怎么处理
测试源码tst_xxx.cpp、.pro、CMakeLists.txt单测用例入口构建、运行、扩展
被测代码src/ 下的 .cpp/.h 或编译好的静态库测试要链接的对象纳入构建路径
资源与测试数据.qrc、testdata/.json、.csv用例运行时读取的输入确认已编译进资源
构建脚本与说明build.sh、run_tests.bat、README.md记录环境依赖和构建顺序按说明复现,但别轻信版本号

一个很容易踩的坑是:压缩包里混进了 build/ 目录,里面是一堆编译中间文件和 CMakeCache.txt。这种包通常是开发者直接从工作目录打包的,没做清理。收到这种包后,构建前最好把 build 目录删掉或重命名,否则 CMake 缓存了旧机器的绝对路径,换个目录解压后配置阶段就会报错。我一般先看有没有顶层独立目录——有就cd进去再构建,没有就直接在当前目录构建。

2.2 构建体系二选一:.pro 和 CMakeLists.txt 决定后续命令

打开包里的工程配置文件,基本能一眼看出这个测试工程走的是 qmake 还是 CMake。这两套构建体系的取舍很清楚:qmake 简单直接,三四行就能描述一个测试目标,适合小工程和快速复现;CMake 配置稍长,但和 CI、IDE 的集成更顺,适合作为团队长期维护的方案。

qmake 的测试工程 .pro 文件,常见写法是:

# 文件: tst_main.pro QT += testlib widgets CONFIG += testcase console CONFIG -= app_bundle TARGET = tst_main SOURCES = tst_main.cpp # 如果被测代码在同目录的 src 下,可以这么加 INCLUDEPATH += ./src LIBS += -L./build -lappcore

CONFIG += testcase是关键:它让 qmake 生成make check目标,运行 make check 就能执行测试并汇总结果。console让输出走标准输出而不是 Qt 的日志系统,这在 CI 抓日志时很有用。app_bundle只在 macOS 上有意义,关掉是为了避免生成 .app 目录结构。

CMake 版本的最小形态长这样:

# 文件: CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(qtapptest LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) # 关键:自动处理 Q_OBJECT 的 moc 步骤 find_package(Qt6 REQUIRED COMPONENTS Test Widgets) enable_testing() add_executable(tst_main tst_main.cpp) target_link_libraries(tst_main PRIVATE Qt6::Test Qt6::Widgets) add_test(NAME tst_main COMMAND tst_main)

enable_testing()必须写,否则ctest不识别这个测试目标。CMAKE_AUTOMOC也必须开——测试类里几乎都带 Q_OBJECT 宏,不开的话 moc 文件不会生成,链接阶段会报一堆未定义引用。如果包里的 CMakeLists 用的还是qt5_add_resources这类旧函数,说明工程可能是从 Qt 5 迁过来的,构建前先确认本机 Qt 版本兼容。

2.3 三个入口宏:QTEST_MAIN 和它的两个兄弟

Qt Test 提供三个入口宏,作用完全不同,选错会导致测试启动慢,或者干脆起不来。它们的区别体现在“创建了什么应用对象”上:

宏创建的应用对象适用场景启动速度
QTEST_APPLESS_MAIN什么都不创建纯函数逻辑测试最快
QTEST_GUILESS_MAINQCoreApplication需要事件循环、信号槽较快
QTEST_MAINQApplicationQWidget 界面测试最慢

常见误用是把需要事件循环的测试写成QTEST_APPLESS_MAIN,结果QSignalSpy永远等不到信号、QTimer不触发。反过来,纯算法测试用QTEST_MAIN也没什么错,就是白白多创建了一套 GUI 应用环境,在 CI 无显示环境下还会触发平台插件加载。

一个完整的测试类通常是这样的骨架:

// tst_main.cpp #include <QtTest> class TestMain : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void alwaysPass(); }; void TestMain::initTestCase() { qDebug() << "start test"; } void TestMain::cleanupTestCase() { qDebug() << "end test"; } void TestMain::alwaysPass() { QVERIFY(true); } // 三选一: // QTEST_APPLESS_MAIN(TestMain) // 纯逻辑测试用这个 // QTEST_GUILESS_MAIN(TestMain) // 事件循环测试用这个 QTEST_MAIN(TestMain) // 界面测试用这个

QTEST_MAIN(TestMain)会展开成包含 main() 函数的完整代码段,所以测试 .cpp 里不能再写自己的 main()。initTestCase和cleanupTestCase在整个测试类运行前后各执行一次,适合做公共环境的准备和清理。

3. 解压验证:从 zip 到第一条 PASS 输出

建模做完了,回到 QtAppTest220104.zip 本身。这个阶段的目标只有一个:把它跑起来,看到第一条 PASS。整个过程拆成三步:先侦察包结构,再按构建体系选命令,最后读测试输出判断结果。这三步做完,你对这个包就有了确切判断:它能不能用、缺不缺东西、值不值得继续投入。

3.1 不解压也能侦察:unzip -l 先看结构

解压是破坏性操作吗?某种程度上是——一个包含几十个文件的 zip 解错目录,后面清理起来很烦。所以我总是先列目录,再决定怎么解开。

unzip -l QtAppTest220104.zip | head -60

unzip -l只列出压缩包内的文件清单,不会真正落盘。head -60限制输出行数,避免文件太多刷屏。这一步要重点看三件事:第一,顶层有没有独立目录,决定解压后要不要cd;第二,有没有CMakeLists.txt或.pro文件,决定走哪套构建命令;第三,有没有混入build/、.git/目录,如果混入了先记下位置,解压后单独清理。

实际处理时常见两种情况:包内文件全部散落在根目录,这种直接在当前目录构建即可;包内有一个顶层文件夹,我一般unzip之后cd进这个文件夹再操作。顺便说一句,如果你收到的包里连tst_*.cpp和构建配置文件都没有,只有一堆 .log 和报告文件,那它不是源码工程包,而是打包好的测试产物,没必要按下面的构建步骤走,直接看报告文件内容就行。

3.2 两种构建体系的最小跑通命令

确定是源码工程包后,构建命令取决于 2.2 里的探测结果。CMake 工程的完整流程:

mkdir -p ~/work/qttest && cd ~/work/qttest unzip ~/Downloads/QtAppTest220104.zip cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug cmake --build build ctest --test-dir build --output-on-failure

-S . -B build指定源码目录和构建目录分开,这是 CMake 的推荐用法;-G Ninja选 Ninja 生成器,比默认的 Makefile 快不少;--output-on-failure让 ctest 在用例失败时把完整日志打出来,CI 里定位问题全靠这个参数。

qmake 工程则是另一套命令:

qmake6 tst_main.pro make -j$(nproc) make check

make check是CONFIG += testcase生成的附加目标,它会运行测试二进制并汇总结果。如果 .pro 没加 testcase,make check会报“没有这个目标”,这时候直接运行生成的测试二进制也可以,比如./tst_main。qmake6是 Qt 6 的 qmake 命令名,某些发行版上仍然叫qmake,运行时注意区分。构建过程如果报Q_OBJECT相关的链接错误,多半是工程文件里漏了CONFIG += ...或 CMake 的 AUTOMOC 没打开,先检查工程配置,不要急着改代码。

3.3 读懂测试输出和退出码:PASS、FAIL、SKIP 的含义

跑通之后,下一步就是读懂输出。Qt Test 的文本输出格式是固定的,一段典型的全量输出:

./tst_main -v1

-v1表示 verbose 级别 1,会显示每个用例的完整名字和执行状态,而不是只汇总结果。输出的大致样子是:

********* Start testing of TestMain ********* Config: Using QtTest library 6.4.2 PASS : TestMain::initTestCase() PASS : TestMain::alwaysPass() PASS : TestMain::cleanupTestCase() Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted ********* Finished testing of TestMain *********

Totals 一行是判断成败的关键:passed是通过的用例数,failed是失败的,skipped是被主动跳过的,blacklisted是被临时排除的。用例失败时,输出里会把实际值和期望值都打出来,比如下面的形式:

FAIL! : TestMain::testSum() Compared values are not the same Actual (actualSum): 5 Expected (expectedSum): 6

看到这种输出,先别急着改代码。测试失败只说明“实际输出和预期不符”,原因可能是被测代码有问题,也可能是测试数据写错了。我是先确认期望值本身没错,再去看被测代码——很多人在这里白调了半天 bug,最后发现是测试数据抄错了。

退出码在命令行里用echo $?查看。0 表示全部通过,非 0 表示至少一个用例失败。这个机制让 Qt 测试可以无缝接入任何 CI 系统,因为 CI 判断成功与否就是看退出码。

4. 把用例写厚:数据驱动、信号验证与界面模拟

跑通一个空测试类只是起点。真正值得维护的 Qt 测试,不是那种“打印一行日志然后 QVERIFY(true)”的占位用例,而是覆盖了正常路径、边界值和异常输入的用例。这章讲三类我几乎天天在用的写法:数据驱动把一函数测到吐、QSignalSpy 验证信号不靠肉眼、模拟输入覆盖交互路径。

4.1 数据驱动测试:_data 函数与 QFETCH 的配合

实际项目里最简单的一类测试是“同一个函数,喂不同输入,断言不同输出”。如果每个输入都写一个测试函数,代码会淹没在复制粘贴里。Qt Test 的数据驱动机制解决的就是这个问题:一个测试函数配一个同名_data函数,_data负责准备数据表,测试函数从表里取数据执行。

// 被测函数:把逗号分隔的数字字符串求和 int sumCsv(const QString &csv); void TestMain::sumCsv_data() { QTest::addColumn<QString>("csv"); QTest::addColumn<int>("expected"); QTest::newRow("empty string") << QString("") << 0; QTest::newRow("single number") << QString("42") << 42; QTest::newRow("multiple numbers") << QString("1,2,3") << 6; QTest::newRow("trailing comma") << QString("1,2,") << 3; } void TestMain::sumCsv() { QFETCH(QString, csv); QFETCH(int, expected); QCOMPARE(sumCsv(csv), expected); }

addColumn声明数据表的列名和类型,newRow添加一行数据,参数类型和顺序必须与addColumn完全对应。QFETCH把当前行的数据取到局部变量,之后就是普通的断言写法。

跑这个测试时,每一行数据都算独立的用例,输出里会用 newRow 的第一个参数标识:PASS : TestMain::sumCsv(empty string)。哪个数据行挂了一目了然。这里有个细节:_data函数的名字必须是“测试函数名 + _data”,少一个下划线都不生效,Qt Test 不报错,只是静默地只跑一次测试、数据为空。我在这上面吃过亏,排查了很久才发现是命名拼错了。

4.2 验证信号不靠肉眼:QSignalSpy 的正确用法

GUI 测试里最常见的需求是“执行一个操作,然后等某个信号触发,再检查信号带的参数”。新手喜欢用QTest::qWait(500)固定等半秒,然后检查界面状态——这在高负载 CI 机器上非常不可靠。正确做法是用 QSignalSpy 挂在信号上,等待并捕获参数。

// 被测对象:网络请求管理器 class NetworkManager : public QObject { Q_OBJECT public: void request(const QUrl &url); signals: void finished(int statusCode); }; // 测试用例 void TestMain::requestFinished() { NetworkManager manager; QSignalSpy spy(&manager, &NetworkManager::finished); manager.request(QUrl("https://example.com/api")); // 关键:用超时等待而不是固定等待 QTRY_VERIFY_WITH_TIMEOUT(spy.count() >= 1, 3000); QList<QVariant> arguments = spy.takeFirst(); int status = arguments.at(0).toInt(); QCOMPARE(status, 200); }

QTRY_VERIFY_WITH_TIMEOUT(spy.count() >= 1, 3000)的含义是:每 50ms 检查一次 spy 收到的信号数量,直到满足条件或 3 秒超时。相比固定qWait,它的优势是机器快就快跑、机器慢就多等一会,用例不会因为调度抖动而随机失败。spy.takeFirst()取出第一次信号携带的参数列表,按信号定义顺序排列,这里finished带一个 int 状态码,所以取arguments.at(0)。

一个经常遇到的坑:如果信号有重载,比如finished(int)和finished(QString),直接写&NetworkManager::finished会编译报错,因为编译器不知道你要哪个。这时候要用qOverload<int>(&NetworkManager::finished)显式指定版本。

4.3 模拟键盘和鼠标:QTest::keyClick 与 mouseClick

界面交互测试的下一层是模拟用户操作。Qt Test 提供了一套完整的输入模拟 API,核心是两个函数:QTest::keyClick和QTest::mouseClick。

void TestMain::loginFlow() { QWidget window; auto *hostEdit = new QLineEdit(&window); hostEdit->setObjectName("hostEdit"); auto *connectBtn = new QPushButton("Connect", &window); connectBtn->setObjectName("connectBtn"); window.show(); // 模拟在 QLineEdit 里输入文本 QTest::keyClick(hostEdit, Qt::Key_H); QTest::keyClicks(hostEdit, "ostName"); // 模拟点击按钮 QTest::mouseClick(connectBtn, Qt::LeftButton); }

keyClick是单击一个按键,keyClicks是连续输入一串字符,适合输入主机名这类文本。注意keyClick的第三个参数是修饰键,比如Qt::ShiftModifier可以输入大写。mouseClick的默认点击位置是控件中心,如果你的控件在边缘区域才有响应,需要显式传入坐标点,比如button->rect().center()。

这种测试能在 CI 上跑的前提是测试类使用了QTEST_MAIN,因为模拟输入依赖真实的 QApplication 事件循环。如果你用QTEST_GUILESS_MAIN创建测试窗口,运行时大概率会崩溃或事件不生效。另外有个实际经验:模拟输入之前控件必须show()并进入事件循环,否则点击坐标计算会出问题。

4.4 浮点比较和异常断言:QCOMPARE 的边界之外

QCOMPARE 好用,但不是万能的。两个最容易出事的地方是浮点比较和异常断言。

// 浮点比较:不要直接 QCOMPARE double ratio = calculateRatio(); QVERIFY(qAbs(ratio - 3.14) < 1e-5); // 异常断言:确定会抛异常的场景 QVERIFY_EXCEPTION_THROWN(parseBadInput(), ParseException);

浮点数的二进制表示决定了3.14 == 3.14可能为 false,直接 QCOMPARE 会导致用例随机失败。常见的替代是qFuzzyCompare,但它对接近 0 的值不友好——两个都接近 0 的数可能被误判为相等。我个人的习惯是用qAbs(actual - expected) < epsilon,epsilon 根据你的数据精度定,1e-5 或 1e-6 是常用量级。

QVERIFY_EXCEPTION_THROWN(expression, ExceptionType)的语义是:如果表达式没抛异常,用例失败;如果抛了但类型不匹配,也失败;只有抛出的异常类型完全匹配才通过。这个宏在写解析器、配置加载器这类健壮性测试时是刚需。

5. Qt 测试避坑:五个雷区与排查路径

Qt 测试框架本身不难,但把它放到真实的工程环境里,各种环境问题和状态污染问题就冒出来了。这一章写我踩过、也帮别人排查过的五个高频雷区,按“现象 → 原因 → 解决”的路径讲,你可以直接当排查手册用。

5.1 GUI 测试在服务器上跑不起来:xcb 插件加载失败

现象:测试编译正常,但在无图形界面的服务器或 CI 容器里运行时报错退出,错误信息类似:

qt.qpa.plugin: Could not find the Qt platform plugin "xcb" in ""

原因:QTEST_MAIN创建的 QApplication 需要加载平台插件,默认是 xcb。服务器上没有 X 服务,也没装 Qt 的 xcb 平台插件包,应用启动阶段就崩了,根本不是测试代码的问题。

解决:测试环境不开图形界面时,指定 offscreen 平台插件:

QT_QPA_PLATFORM=offscreen ./tst_main -v1

offscreen是 Qt 自带的无头平台插件,不依赖 X 服务,适用于绝大多数 QWidget 测试。如果你的测试里涉及QOpenGLWidget,offscreen 可能不够,那时就得用xvfb-run ./tst_main起一个虚拟 X 服务来跑。排查顺序别搞反:先试 offscreen,不行再上 xvfb。还有一个相关坑:本机能跑、服务器不能跑,先检查服务器的 Qt 安装是否完整,别上来就改测试代码。

5.2 用例互相污染:单独跑全过,一起跑翻车

现象:./tst_main -v1 testOne单独跑一个用例,通过;./tst_main跑全部用例,testOne 失败。或者反过来:单独跑失败,全量跑反而通过。

原因:测试类里可能定义了静态成员或全局单例,某个用例改了状态但没还原,下一个用例读到了脏数据。更隐蔽的是,如果测试操作了文件系统或环境变量,比如某个用例创建了临时文件但清理不干净,后面的用例一读就炸。

解决:每个用例需要独立状态时,用init()和cleanup()这两个特殊槽,它们会在每个用例执行前后各跑一次;initTestCase和cleanupTestCase只跑一次,不要在里面放会被用例修改的状态。另外检查代码里是否有 static 局部变量,该改成成员变量的就改。class 内 private slots 的执行顺序就是声明顺序,所以别依赖用例之间的执行顺序,每个用例都应该是自包含的。

5.3 中文路径下 qmake 工程构建失败

现象:工程放在D:\测试工程\qtapptest\或/home/用户/测试目录/这类含非 ASCII 字符的路径下,qmake 配置成功,make 到一半报错,或者生成的测试二进制运行异常。

原因:qmake 生成的 Makefile 对非 ASCII 路径的处理不够稳健,源文件路径含中文时,编译器的依赖规则可能生成错误的文件路径;即使编译过了,运行时 Qt 查找插件和资源的路径也可能因为编码问题错位。

解决:最省心的办法是构建前把工程统一放到纯英文、无空格的路径下,比如/home/ci/work/qtapptest。这个习惯无论对 qmake 还是 CMake 都有用——CMake 对中文路径支持比 qmake 好一些,但底下的编译器(比如 MSVC)对源文件路径的编码判断依然可能出问题。如果你在 Windows 上,还有一个连锁坑:构建目录里会生成大量中间文件,路径过长也会触发“文件路径超长”错误,所以解压路径尽量短,顶层目录不要套好几层。

5.4 固定等待随机超时:用超时断言代替 qWait

现象:测试用例里用QTest::qWait(500)等待某个异步操作完成,本机跑十次过十次,CI 上跑十次挂三回,错误全是超时或状态不对。

原因:qWait(500)的逻辑是“固定等 500ms,不管事情做没做完”。CI 机器负载高,500ms 可能不够;本机性能好,200ms 就完成了。固定等待的本质是把性能差异变成随机失败源。

解决:把“固定等待”改成“轮询条件”。用QTRY_VERIFY或QTRY_VERIFY_WITH_TIMEOUT替代:

// 不要这么写 QTest::qWait(500); QVERIFY(spy.count() == 1); // 改成这样 QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 1, 3000);

QTRY_VERIFY会每 50ms 检查一次条件,默认超时是 5 秒;带_WITH_TIMEOUT的版本可以自定义超时。这样机器慢就多等,机器快就早退,用例稳定性立刻上一个台阶。如果你等的是某个信号,也可以直接用QSignalSpy::wait(ms)——它的返回值就告诉你信号是否在超时前到达,比手工轮询更简洁。

5.5 资源文件在测试里读不到:qrc 没编译进去

现象:测试代码里写QFile file(":/testdata/config.json"),运行时报文件不存在,或者file.open()返回 false。但同样的代码在被测应用里跑得好好的。

原因:qrc 资源文件没有被编译进测试二进制。qmake 工程里 .pro 缺了RESOURCES += xxx.qrc;CMake 工程里缺了qt_add_resources或qt5_add_resources调用。Qt 资源系统是按目标编译的,测试是一个独立可执行文件,必须单独把资源加进去。

解决:先在构建日志里搜有没有rcc相关输出——rcc 是 Qt 资源编译器,如果它根本没出现在日志里,就是工程配置问题。qmake 工程检查 .pro:

RESOURCES += testdata.qrc

CMake 工程在 CMakeLists.txt 里加:

qt_add_resources(tst_main "testdata" PREFIX "/" FILES ${CMAKE_CURRENT_SOURCE_DIR}/testdata/config.json )

注意资源路径的构建规则:.qrc文件里声明的路径是相对文件系统路径,但运行时访问的路径是:/前缀加 qrc 里的 alias。一个调试小技巧:进QTest::qExec前用QFileInfo(":/")列一下根路径,能快速确认资源到底有没有进二进制。

6. 让 220104 这类日期包变成团队资产:快照清单与一键回归

命名里带日期的 zip,本质是一个快照。快照的价值在“时间切片上的可复现状态”:某个版本的应用、某一天的测试代码、当时的环境,都冻结在里面。但这个价值有一个前提——拿到包的人能快速重建环境并跑起来。否则包解开了,测试也跑了,但没人能说清这个包当时是怎么构建的,值不值得信任。我自己的习惯是,在工程根目录放一个 buildinfo.txt,每次打包测试 zip 时顺手把关键信息写进去。内容不复杂,就四行:

Qt版本: 6.4.2 编译器: GCC 11.2 构建系统: CMake 3.24 验证结果: 32 passed, 0 failed

这个文件的作用是让两个月后的自己或新接手的人不用考古。220104 这个日期只能告诉你打包时间,buildinfo.txt 才能告诉你“这个包在什么环境下验证过”。

配合快照的另一个习惯是一键回归脚本,把它放在包的顶层目录:

#!/usr/bin/env bash # 一键回归:解压 -> 配置 -> 构建 -> 测试 -> 打印结果 set -euo pipefail ZIP="$1" WORK="${ZIP%.zip}-work" unzip -q "$ZIP" -d "$WORK" cmake -S "$WORK" -B "$WORK/build" -G Ninja -DCMAKE_BUILD_TYPE=Debug cmake --build "$WORK/build" ctest --test-dir "$WORK/build" --output-on-failure

set -euo pipefail的作用是任一命令失败立即退出、变量必须预定义、管道中途出错不会静默,没有这三条,构建中途挂了脚本可能还报“成功”。脚本虽然短,但它把“解压到哪、怎么配置、怎么跑”固化成了命令,任何人拿到包都能复现,而不是靠猜。

我在这上面吃过一次亏:某次线上问题需要反查旧版本行为,当时找不到半年前的测试包对应环境,只能凭印象重搭,浪费了大半天。从那以后,每次交付测试快照都强制带 buildinfo.txt 和一键脚本,这个习惯一直保持到现在。希望这篇笔记能让你在拿到下一个 QtAppTest 开头的压缩包时,少走几步弯路,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询