QtTest 是 Qt 官方提供的一个轻量级、易用的 C++ 单元测试框架,主要用于测试基于 Qt 开发的应用程序和库。它集成在 Qt 库中,无需额外安装,只需在项目的.pro文件中加入下面两句就可以通过 #include <QtTest> 语句来使用这个框架

QT       +=  testlib
CONFIG   += qtestlib

在Qt Test框架中,测试类能够直接定义槽函数而无需手动connect连接信号与槽,并且也不需要定义信号,也就是说槽函数并不需要信号触发这得益于Qt Test框架内部的自动化机制Qt元对象系统(Meta-Object System)的结合。以下是详细解释:

1. Qt Test的自动化槽函数调用机制

Qt Test框架通过以下方式自动识别并调用测试槽函数:

  • 命名约定:测试函数必须声明在private slots区域,且通常以test为前缀(如testResponseTime)。

  • 反射机制:Qt的元对象系统(通过Q_OBJECT宏和moc生成)会收集所有槽函数,供Qt Test框架动态调用。

  • 框架控制QTest::qExec()会自动遍历测试类中的所有测试槽函数并按运行逻辑顺序执行。举例:

#ifndef TCP_TIMING_TEST_H
#define TCP_TIMING_TEST_H

#include <QObject>
#include <QtTest>
#include "server.h"
#include "client.h"

class TcpTimingTest : public QObject
{
    Q_OBJECT  // 必须包含这个宏
public:
    explicit TcpTimingTest(QObject *parent = nullptr);

private slots:  // 必须使用这个标签
    void initTestCase();
    void cleanupTestCase();
    void testFrameInterval();
    void testResponseTime();

private:
    Server m_server;
    Client m_client;
    quint16 m_port;
};

#endif // TCP_TIMING_TEST_H

在上面的例子中,private slots中定义的槽函数会按运行的逻辑(先初始化,后测试,最后资源释放/清理)顺序执行,不需要额外的信号触发和连接

2. 与常规的Qt信号槽相比,Qt Test 测试槽函数并不需要运行事件循环QCoreApplication::exec),测试框架可以直接调用,举个例子,对于前者,main函数中应该有QCoreApplication a(argc, argv); 语句 才能够使用QObject对象

int main(int argc, char *argv[])
{
        QCoreApplication a(argc, argv);

        // 正常业务逻辑
        quint16 port = 8888;
        Server server;
        if (!server.listen(QHostAddress::Any, port)) {
            qDebug() << "Failed to start server:" << server.errorString();
            return -1;
        }

        Client client;
        if (!client.connectToHost(QHostAddress::LocalHost, port)) {
            qDebug() << "Failed to connect to server";
            return -1;
        }

        QTimer::singleShot(1000, [&client]() {
            QByteArray testData;
            quint8 functionCode = 0x03;
            client.controlInstruction(functionCode, testData);
            client.sendData(testData);
        });

        return a.exec();
 }

但对于Qt Test测试用例,只需要一句话就可以执行main函数的功能,括号中的参数是笔者定义的测试用例的类的名称,需要注意的是,这句话既可以单独创建一个main文件运行,也可以直接放在定义的测试用例类cpp文件的最后。

QTEST_MAIN(TcpTimingTest) //测试程序的运行函数,和main函数功能是一样的,所以两者只能运行一个,不能同时运行。

3. 为什么不需要手动connect

  • 框架内部实现QTest::qExec()会通过以下步骤处理测试类:

    1. 利用QMetaObject获取所有private slots中的函数。

    2. 按顺序直接调用这些函数(类似反射调用)。(大多数情况下声明顺序与执行顺序一致)

  • 非事件驱动:测试槽函数是同步执行的,不依赖Qt的事件循环,因此无需信号触发。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐