Qt 单元测试的用法
一、编写单元测试
本节是关于如何编写一个简单的单元测试类,以及如何执行测试。
假设想测试 QString 类的行为。首先,需要一个包含测试函数的类,这个类必须继承自 QObject:
-
-
-
class TestQString: public QObject
-
{
-
Q_OBJECT
-
private slots:
-
void toUpper();
-
};
需要包含 QTest 头文件并将测试函数声明为私有槽函数,以便测试框架找到并执行它。
测试函数实现:
-
void TestQString::toUpper()
-
{
-
QString str = "Hello";
-
QVERIFY(str.toUpper() == "HELLO");
-
}
QVERIFY() 宏计算作为其参数传递的表达式。表达式的结果:
- 为 true 则继续执行测试函数。
- 为 false 则向测试日志添加描述失败的消息,并且测试函数会停止执行。
测试失败时:
测试成功时:
如果想要更详细的测试日志输出,可以改用 QCOMPARE() 宏:
-
void TestQString::toUpper()
-
{
-
QString str = "Hello";
-
QCOMPARE(str.toUpper(), QString("HELLO"));
-
}
使用 QCOMPARE() 宏测试成功时输出和上面一样。测试失败时会显示更多信息:
最后,为了使测试用例成为一个独立的可执行文件,需要以下两行:
-
QTEST_MAIN(TestQString) //放在.cpp文件
-
QTEST_MAIN() 宏扩展为运行所有测试函数的简单 main() 方法。如果测试类的声明和实现都在一个 .cpp 文件中,还需要包含生成的 moc 文件以使 Qt 的反射机制工作。
二、使用不同的测试数据多次执行测试
如果想要添加更多测试数据,如下:
-
QCOMPARE(QString("hello").toUpper(), QString("HELLO"));
-
QCOMPARE(QString("Hello").toUpper(), QString("HELLO"));
-
QCOMPARE(QString("HellO").toUpper(), QString("HELLO"));
-
QCOMPARE(QString("HELLO").toUpper(), QString("HELLO"));
为了防止函数最终被重复代码弄乱,Qt Test 支持将测试数据添加到测试函数中。只需要在测试类中添加另一个私有槽:
-
class TestQString: public QObject
-
{
-
Q_OBJECT
-
-
private slots:
-
void toUpper_data();
-
void toUpper();
-
};
测试函数的关联数据函数具有相同的名称,并附加了 _data:
-
void TestQString::toUpper_data()
-
{
-
QTest::addColumn<QString>("string");
-
QTest::addColumn<QString>("result");
-
-
QTest::newRow("all lower") << "hello" << "HELLO";//数据集 all lower
-
QTest::newRow("mixed") << "Hello" << "HELLO";//数据集 mixed
-
QTest::newRow("all upper") << "HELLO" << "HELLO";//数据集 all upper
-
}
这里使用 QTest::addColumn() 函数定义测试表的两个元素:一个测试字符串,以及将 QString::toUpper() 函数应用于该字符串的预期结果。
然后使用 QTest::newRow() 函数将一些数据添加到表中。每组数据将成为测试表中的单独行。
QTest::newRow() 接受一个参数:将与数据集关联并在测试日志中用于标识数据集的名称。然后将数据集流式传输到新的表行中。首先是一个任意字符串,然后将 QString::toUpper() 函数应用于该字符串的预期结果。
可以将测试数据视为一个二维表。上面的测试数据可视为下表。此外,数据集的名称和索引与每一行相关联:
当数据流入行时,每个数据都被断言以匹配它提供的值的列的类型。如果任何断言失败,则中止测试。
测试函数重写为:
-
void TestQString::toUpper()
-
{
-
QFETCH(QString, string);
-
QFETCH(QString, result);
-
-
QCOMPARE(string.toUpper(), result);
-
}
TestQString::toUpper() 函数将执行 3 次,对在关联的 TestQString::toUpper_data() 函数中创建的测试表中的每个条目执行一次。
首先,使用 QFETCH() 宏获取数据集的两个元素。QFETCH() 接受两个参数:元素的数据类型和元素名称。然后使用 QCOMPARE() 宏执行测试。
这种方法可以很容易地将新数据添加到测试中,而无需修改测试代码。
代码汇总:
-
-
-
class TestQString: public QObject
-
{
-
Q_OBJECT
-
-
private slots:
-
void toUpper_data();
-
void toUpper();
-
};
-
-
void TestQString::toUpper_data()
-
{
-
QTest::addColumn<QString>("string");
-
QTest::addColumn<QString>("result");
-
-
QTest::newRow("all lower") << "hello" << "HELLO";
-
QTest::newRow("mixed") << "Hello" << "HELLO";
-
QTest::newRow("all upper") << "HELLO" << "HELLO";
-
}
-
-
void TestQString::toUpper()
-
{
-
QFETCH(QString, string);
-
QFETCH(QString, result);
-
-
QCOMPARE(string.toUpper(), result);
-
}
-
-
QTEST_MAIN(TestQString)
-
三、模拟 GUI 事件
Qt Test 具有测试图形用户界面的机制。其原理不是模拟本地窗口系统事件,而是发送内部 Qt 事件。
假设想要测试 QLineEdit 类的行为。需要一个包含测试函数的类:
-
-
-
-
class TestGui : public QObject
-
{
-
Q_OBJECT
-
-
private slots:
-
void testGui();
-
};
-
-
void TestGui::testGui()
-
{
-
QLineEdit lineEdit;
-
QTest::keyClicks(&lineEdit, "hello world");
-
QCOMPARE(lineEdit.text(), QString("hello world"));
-
}
在执行测试函数时首先创建一个 QLineEdit。然后使用 QTest::keyClicks() 函数生成键盘按下事件在QLineEdit 中模拟编写“hello world”。
最后,使用 QCOMPARE() 宏来检查行编辑的文本是否符合预期。
四、存储和重播 GUI 事件
存储一系列事件并重放它们的方法与第 2 节中的方法非常相似。需要做的就是在测试类中添加一个数据函数:
-
class TestGui: public QObject
-
{
-
Q_OBJECT
-
-
private slots:
-
void testGui_data();
-
void testGui();
-
};
和之前一样,测试函数的关联数据函数具有相同的名称,并附加了 _data。
-
void TestGui::testGui_data()
-
{
-
QTest::addColumn<QTestEventList>("events");
-
QTest::addColumn<QString>("expected");
-
-
QTestEventList list1;
-
list1.addKeyClick('a');
-
QTest::newRow("char") << list1 << "a";
-
-
QTestEventList list2;
-
list2.addKeyClick('a');
-
list2.addKeyClick(Qt::Key_Backspace);
-
QTest::newRow("there and back again") << list2 << "";
-
}
首先,使用 QTest::addColumn() 函数定义二维表的元素:GUI 事件列表,以及在 QWidget 上应用事件列表的预期结果。
QTestEventList 可以填充 GUI 事件,这些事件可以存储为测试数据以供以后使用,或者在任何 QWidget 上重放。
这里创建了两个 QTestEventList 元素:
第一个列表由单击“a”键组成。使用 addKeyClick() 函数将事件添加到列表中。然后使用 QTest::newRow() 将此数据集命名为“char”,并将事件列表和预期结果流式传输到表中。
第二个列表由两个键单击组成:一个“a”和一个“退格”。
重写测试函数:
-
void TestGui::testGui()
-
{
-
QFETCH(QTestEventList, events);
-
QFETCH(QString, expected);
-
-
QLineEdit lineEdit;
-
-
events.simulate(&lineEdit);
-
-
QCOMPARE(lineEdit.text(), expected);
-
}
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
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24