• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Qt 单元测试的用法

武飞扬头像
友善啊,朋友
帮助1

一、编写单元测试

本节是关于如何编写一个简单的单元测试类,以及如何执行测试。

假设想测试 QString 类的行为。首先,需要一个包含测试函数的类,这个类必须继承自 QObject

  1.  
    #include <QTest>
  2.  
     
  3.  
    class TestQString: public QObject
  4.  
    {
  5.  
    Q_OBJECT
  6.  
    private slots:
  7.  
    void toUpper();
  8.  
    };

需要包含 QTest 头文件并将测试函数声明为私有槽函数,以便测试框架找到并执行它。

测试函数实现:

  1.  
    void TestQString::toUpper()
  2.  
    {
  3.  
    QString str = "Hello";
  4.  
    QVERIFY(str.toUpper() == "HELLO");
  5.  
    }

QVERIFY() 宏计算作为其参数传递的表达式。表达式的结果:

  • 为 true 则继续执行测试函数。
  • 为 false 则向测试日志添加描述失败的消息,并且测试函数会停止执行

测试失败时:

学新通

测试成功时:

学新通

如果想要更详细的测试日志输出,可以改用 QCOMPARE() 宏:

  1.  
    void TestQString::toUpper()
  2.  
    {
  3.  
    QString str = "Hello";
  4.  
    QCOMPARE(str.toUpper(), QString("HELLO"));
  5.  
    }

使用 QCOMPARE() 宏测试成功时输出和上面一样。测试失败时会显示更多信息:

学新通

最后,为了使测试用例成为一个独立的可执行文件,需要以下两行:

  1.  
    QTEST_MAIN(TestQString) //放在.cpp文件
  2.  
    #include "testqstring.moc"

QTEST_MAIN() 宏扩展为运行所有测试函数的简单 main() 方法。如果测试类的声明和实现都在一个 .cpp 文件中,还需要包含生成的 moc 文件以使 Qt 的反射机制工作。

二、使用不同的测试数据多次执行测试

如果想要添加更多测试数据,如下:

  1.  
    QCOMPARE(QString("hello").toUpper(), QString("HELLO"));
  2.  
    QCOMPARE(QString("Hello").toUpper(), QString("HELLO"));
  3.  
    QCOMPARE(QString("HellO").toUpper(), QString("HELLO"));
  4.  
    QCOMPARE(QString("HELLO").toUpper(), QString("HELLO"));

为了防止函数最终被重复代码弄乱,Qt Test 支持将测试数据添加到测试函数中。只需要在测试类中添加另一个私有槽:

  1.  
    class TestQString: public QObject
  2.  
    {
  3.  
    Q_OBJECT
  4.  
     
  5.  
    private slots:
  6.  
    void toUpper_data();
  7.  
    void toUpper();
  8.  
    };

测试函数关联数据函数具有相同的名称,并附加了 _data

  1.  
    void TestQString::toUpper_data()
  2.  
    {
  3.  
    QTest::addColumn<QString>("string");
  4.  
    QTest::addColumn<QString>("result");
  5.  
     
  6.  
    QTest::newRow("all lower") << "hello" << "HELLO";//数据集 all lower
  7.  
    QTest::newRow("mixed") << "Hello" << "HELLO";//数据集 mixed
  8.  
    QTest::newRow("all upper") << "HELLO" << "HELLO";//数据集 all upper
  9.  
    }

这里使用 QTest::addColumn() 函数定义测试表的两个元素:一个测试字符串,以及将 QString::toUpper() 函数应用于该字符串的预期结果。

然后使用 QTest::newRow() 函数将一些数据添加到表中。每组数据将成为测试表中的单独行。

QTest::newRow() 接受一个参数:将与数据集关联并在测试日志中用于标识数据集的名称。然后将数据集流式传输到新的表行中。首先是一个任意字符串,然后将 QString::toUpper() 函数应用于该字符串的预期结果。

可以将测试数据视为一个二维表。上面的测试数据可视为下表。此外,数据集的名称和索引与每一行相关联:

学新通

当数据流入行时,每个数据都被断言以匹配它提供的值的列的类型。如果任何断言失败,则中止测试。

测试函数重写为:

  1.  
    void TestQString::toUpper()
  2.  
    {
  3.  
    QFETCH(QString, string);
  4.  
    QFETCH(QString, result);
  5.  
     
  6.  
    QCOMPARE(string.toUpper(), result);
  7.  
    }

TestQString::toUpper() 函数将执行 3 次,对在关联的 TestQString::toUpper_data() 函数中创建的测试表中的每个条目执行一次。

首先,使用 QFETCH() 宏获取数据集的两个元素。QFETCH() 接受两个参数:元素的数据类型和元素名称。然后使用 QCOMPARE() 宏执行测试。

这种方法可以很容易地将新数据添加到测试中,而无需修改测试代码。

学新通

代码汇总:

  1.  
    #include <QTest>
  2.  
     
  3.  
    class TestQString: public QObject
  4.  
    {
  5.  
    Q_OBJECT
  6.  
     
  7.  
    private slots:
  8.  
    void toUpper_data();
  9.  
    void toUpper();
  10.  
    };
  11.  
     
  12.  
    void TestQString::toUpper_data()
  13.  
    {
  14.  
    QTest::addColumn<QString>("string");
  15.  
    QTest::addColumn<QString>("result");
  16.  
     
  17.  
    QTest::newRow("all lower") << "hello" << "HELLO";
  18.  
    QTest::newRow("mixed") << "Hello" << "HELLO";
  19.  
    QTest::newRow("all upper") << "HELLO" << "HELLO";
  20.  
    }
  21.  
     
  22.  
    void TestQString::toUpper()
  23.  
    {
  24.  
    QFETCH(QString, string);
  25.  
    QFETCH(QString, result);
  26.  
     
  27.  
    QCOMPARE(string.toUpper(), result);
  28.  
    }
  29.  
     
  30.  
    QTEST_MAIN(TestQString)
  31.  
    #include "testqstring.moc"
学新通

三、模拟 GUI 事件

Qt Test 具有测试图形用户界面的机制。其原理不是模拟本地窗口系统事件,而是发送内部 Qt 事件。

假设想要测试 QLineEdit 类的行为。需要一个包含测试函数的类:

  1.  
    #include <QtWidgets>
  2.  
    #include <QTest>
  3.  
     
  4.  
    class TestGui : public QObject
  5.  
    {
  6.  
    Q_OBJECT
  7.  
     
  8.  
    private slots:
  9.  
    void testGui();
  10.  
    };
  11.  
     
  12.  
    void TestGui::testGui()
  13.  
    {
  14.  
    QLineEdit lineEdit;
  15.  
    QTest::keyClicks(&lineEdit, "hello world");
  16.  
    QCOMPARE(lineEdit.text(), QString("hello world"));
  17.  
    }
学新通

在执行测试函数时首先创建一个 QLineEdit。然后使用 QTest::keyClicks() 函数生成键盘按下事件在QLineEdit 中模拟编写“hello world”。

最后,使用 QCOMPARE() 宏来检查行编辑的文本是否符合预期。

学新通

四、存储和重播 GUI 事件

存储一系列事件并重放它们的方法与第 2 节中的方法非常相似。需要做的就是在测试类中添加一个数据函数:

  1.  
    class TestGui: public QObject
  2.  
    {
  3.  
    Q_OBJECT
  4.  
     
  5.  
    private slots:
  6.  
    void testGui_data();
  7.  
    void testGui();
  8.  
    };

和之前一样,测试函数的关联数据函数具有相同的名称,并附加了 _data

  1.  
    void TestGui::testGui_data()
  2.  
    {
  3.  
    QTest::addColumn<QTestEventList>("events");
  4.  
    QTest::addColumn<QString>("expected");
  5.  
     
  6.  
    QTestEventList list1;
  7.  
    list1.addKeyClick('a');
  8.  
    QTest::newRow("char") << list1 << "a";
  9.  
     
  10.  
    QTestEventList list2;
  11.  
    list2.addKeyClick('a');
  12.  
    list2.addKeyClick(Qt::Key_Backspace);
  13.  
    QTest::newRow("there and back again") << list2 << "";
  14.  
    }

首先,使用 QTest::addColumn() 函数定义二维表的元素:GUI 事件列表,以及在 QWidget 上应用事件列表的预期结果。

QTestEventList 可以填充 GUI 事件,这些事件可以存储为测试数据以供以后使用,或者在任何 QWidget 上重放。

这里创建了两个 QTestEventList 元素:

第一个列表由单击“a”键组成。使用 addKeyClick() 函数将事件添加到列表中。然后使用 QTest::newRow() 将此数据集命名为“char”,并将事件列表和预期结果流式传输到表中。

第二个列表由两个键单击组成:一个“a”和一个“退格”。

重写测试函数:

  1.  
    void TestGui::testGui()
  2.  
    {
  3.  
    QFETCH(QTestEventList, events);
  4.  
    QFETCH(QString, expected);
  5.  
     
  6.  
    QLineEdit lineEdit;
  7.  
     
  8.  
    events.simulate(&lineEdit);
  9.  
     
  10.  
    QCOMPARE(lineEdit.text(), expected);
  11.  
    }

TestGui::testGui() 函数将被执行两次,对于在关联的 TestGui::testGui_data() 函数中创建的测试数据中的每个条目执行一次。

首先,使用 QFETCH() 宏获取数据集的两个元素。然后创建一个 QLineEdit,并使用 QTestEventList::simulate() 函数在该小部件上应用事件列表。

最后,使用 QCOMPARE() 宏来检查行编辑的文本是否符合预期。

学新通

五、使用 QSKIP 跳过测试

如果从测试函数中调用 QSKIP() 宏,它会停止测试的执行,而不会将失败信息添加到测试日志中。QSKIP() 描述参数中的文本附加到测试日志中,并解释了未执行测试的原因。

当测试尚未完成或在某个平台上不受支持的功能时,QSKIP() 可用于跳过测试。

当存在已知故障时,可使用 QEXPECT_FAIL(),因为它支持在可能的情况下运行其余的测试。

学新通

如果从 _data 函数调用,QSKIP() 宏将停止执行 _data 函数。这会阻止执行相关的测试功能。

学新通

如果从 initTestCase() 或 initTestCase_data() 调用,QSKIP() 宏将跳过所有测试和 _data 函数。

六、单元测试相关的函数

要创建测试,需要将 QObject 子类化并为其添加一个或多个私有槽。每个私有槽都是测试中的一个测试函数。QTest::qExec() 可用于执行测试对象中的所有测试函数

此外,可以定义以下不被视为测试函数的私有槽。如果存在,它们将由测试框架执行,可用于初始化和清理整个测试或当前测试功能。

  • initTestCase():将在第一个测试函数执行之前被调用。
  • initTestCase_data():将调用它来创建全局测试数据表
  • cleanupTestCase():将在最后一个测试函数执行后被调用。
  • init():将在每个测试函数执行之前被调用。
  • cleanup():将在每个测试函数之后调用。

如果 initTestCase() 失败,则不会执行任何测试函数。

如果 init() 失败,则不会执行下面的测试函数,将继续下一个测试函数。

最后,如果测试类有一个静态的 public void initMain() 方法,它会在 QApplication 对象被实例化之前由 QTEST_MAIN() 宏调用。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfkhhie
系列文章
更多 icon
同类精品
更多 icon
继续加载