一、描述
QTest 命名空间包含与 Qt 单元测试相关的所有函数和声明。
二、类型成员
1、enum QTest::KeyAction:此枚举描述了按键的可能操作。
Press:按键被按下。
Release:按键被释放。
Click:按键被单击(按下并释放)。
Shortcut:按键的快捷方式被激活。
2、enum QTest::MouseAction:此枚举描述了鼠标的可能操作。
MousePress:按下鼠标按钮。
MouseRelease:释放鼠标按钮。
MouseClick:单击鼠标按钮(按下并释放)。
MouseDClick:双击鼠标按钮(按下并释放两次)。
MouseMove:鼠标指针已移动。
3、enum QTest::TestFailMode:此枚举描述了检查到错误后的处理模式,例如通过 QVERIFY() 或 QCOMPARE() 宏,这是已知的失败。
Abort:中止测试的执行。如果检查到问题后继续执行测试没有意义时可使用此模式。
Continue:在检查发现问题后继续执行测试。
三、部分成员函数
1、template void addColumn(const char *name, T *dummy = 0)
添加类型为 T 的列作为当前测试数据。name 是列的名称。dummy 是编译器错误的一种解决方法,可以忽略。
QTest::addColumn<int>("intval");
QTest::addColumn<QString>("str");
QTest::addColumn<double>("dbl");
在实际测试中使用 QFETCH() 来获取数据。
要将自定义类型添加到测试数据,该类型必须通过 Q_DECLARE_METATYPE() 向 QMetaType 注册。
2、QTestData & addRow(const char *format, ...)
将新行追加到当前测试数据。函数的参数被传递给 qsnprintf() 以根据格式进行格式化。
QTest::addColumn<int>("input");
QTest::addColumn<QString>("output");
QTest::addRow("%d", 0) << 0 << QString("0");
QTest::addRow("%d", 1) << 1 << QString("1");
格式化的字符串将在测试输出中显示为该测试数据的名称。
返回可用于流式传输数据的 QTestData 引用。
QTestData & newRow(const char *dataTag)
将新行追加到当前测试数据。dataTag 是将出现在测试输出中的测试数据的名称。 返回可用于流式传输数据的 QTestData 引用。
void MyTestClass::addSingleStringRows()
{
QTest::addColumn<QString>("aString");
QTest::newRow("just hello") << QString("hello");
QTest::newRow("a null string") << QString();
}
3、const char * currentAppName()
返回当前执行的二进制文件的名称。
4、const char * currentDataTag()
返回当前测试数据的名称。
5、bool currentTestFailed()
返回当前测试函数是否失败。
6、const char * currentTestFunction()
返回当前执行的测试函数的名称。
7、
void failOnWarning(const QRegularExpression &messagePattern)
void failOnWarning(const char *message)
对于与 messagePattern 匹配的每个警告,将测试失败附加到测试日志。
注意:ignoreMessage() 优先于此函数。
void FileTest::loadFiles()
{
QTest::failOnWarning(QRegularExpression("^Failed to load"));
// 这些中的每一个都会导致测试失败:
qWarning() << "Failed to load image";
qWarning() << "Failed to load video";
}
8、
void ignoreMessage(QtMsgType type, const char *message)
void ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern)
忽略由 qDebug()、qInfo() 或 qWarning() 创建的消息。如果输出了对应类型的消息,则会从测试日志中删除该消息。如果测试完成并且没有输出消息,则测试失败将附加到测试日志中。
注意:调用此函数只会忽略一条消息。 如果要忽略的消息被输出两次,也必须调用两次 ignoreMessage()。
QDir dir;
QTest::ignoreMessage(QtWarningMsg, "QDir::mkdir: Empty or null file name(s)");
dir.mkdir("");
上面的代码创建的文件名无效,默认会输出上面的内容,这里设置了测试日志忽略此输出内容。
9、
void keyClick(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
void keyClick(QWindow *window, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
模拟在小部件上使用可选修饰符单击键。如果 delay 大于 0,测试会等待 delay 毫秒再点击按键。
QTest::keyClick(myWidget, Qt::Key_Escape);
QTest::keyClick(myWidget, Qt::Key_Escape, Qt::ShiftModifier, 200);
上面的第一个示例模拟了在没有任何键盘修饰符且没有延迟的情况下单击 myWidget 上的退出键。 第二个示例模拟在测试延迟 200 毫秒后单击 myWidget 上的 shift-escape。
#include <QApplication> #include <QDebug> #include <QTest> #include <QPushButton> #include <QTimer> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget w; auto btn = new QPushButton("按钮",&w); btn->connect(btn,&QPushButton::clicked,[]{ qDebug()<<"按钮按下"; }); btn->setDefault(true); QTimer timer; timer.connect(&timer,&QTimer::timeout,[btn]{ QTest::keyClick(btn, Qt::Key_Space); }); timer.start(1000); w.resize(300,300); w.show(); return a.exec(); } void keyClick(QWindow *window, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1) void keyClick(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1) QTest::keyClick(myWidget, 'a');
10、
void keyClicks(QWidget *widget, const QString &sequence, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
模拟单击小部件上的一系列按键。 void Widget::keyPressEvent(QKeyEvent *event) { qDebug()<<QChar(event->key()); } int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; QTimer timer; timer.connect(&timer,&QTimer::timeout,[&] { QTest::keyClicks(&w, "hello world!"); }); timer.start(1000); w.resize(300,300); w.show(); return a.exec(); }
11、
void keyEvent(QTest::KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1) void keyEvent(QTest::KeyAction action, QWindow *window, char ascii, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1) void keyEvent(QTest::KeyAction action, QWindow *window, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1) void keyEvent(QTest::KeyAction action, QWidget *widget, char ascii, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
使用 key 键和关联的 action 向小部件发送 Qt 键事件。可以指定键盘修饰符,以及发送事件之前的测试延迟(以毫秒为单位)。
Widget w; QTimer timer; timer.connect(&timer,&QTimer::timeout,[&] { QTest::keyEvent(QTest::Press,&w,Qt::Key_Space); }); timer.start(1000);
12、
void keyPress(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
void keyPress(QWindow *window, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
void keyPress(QWindow *window, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
void keyPress(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
模拟在小部件上按下带有可选修饰符的 key 键。
注意:在某些时候,应该使用 keyRelease() 释放按键。
13、
void keyRelease(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
void keyRelease(QWindow *window, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
void keyRelease(QWindow *window, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
void keyRelease(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
模拟在小部件上释放带有可选修饰符的 key 键。
14、
void keySequence(QWidget *widget, const QKeySequence &keySequence)
void keySequence(QWindow *window, const QKeySequence &keySequence)
模拟在窗口中触发快捷键。
15、
void mouseClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = Qt::KeyboardModifiers(), QPoint pos = QPoint(), int delay = -1)
void mouseClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = Qt::KeyboardModifiers(), QPoint pos = QPoint(), int delay = -1)
模拟在小部件上单击带有可选修饰符 modifier 的鼠标按钮。点击的位置由pos定义,默认位置是小部件的中心。如果指定了延迟,则测试将在按下和释放按钮之前等待指定的毫秒数。
16、
void mouseDClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = Qt::KeyboardModifiers(), QPoint pos = QPoint(), int delay = -1)
void mouseDClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = Qt::KeyboardModifiers(), QPoint pos = QPoint(), int delay = -1)
模拟在小部件上使用可选修饰符 modifier 双击鼠标按钮。
17、
void mouseMove(QWidget *widget, QPoint pos = QPoint(), int delay = -1)
void mouseMove(QWindow *window, QPoint pos = QPoint(), int delay = -1)
将鼠标指针移动到小部件。如果未指定 pos,鼠标指针将移动到小部件的中心。
18、
void mousePress(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = Qt::KeyboardModifiers(), QPoint pos = QPoint(), int delay = -1)
void mousePress(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = Qt::KeyboardModifiers(), QPoint pos = QPoint(), int delay = -1)
模拟在小部件上使用可选修饰符 modifier 按下鼠标按钮。
19、
void mouseRelease(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = Qt::KeyboardModifiers(), QPoint pos = QPoint(), int delay = -1)
void mouseRelease(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = Qt::KeyboardModifiers(), QPoint pos = QPoint(), int delay = -1)
模拟在小部件上释放按下的鼠标按钮。
20、
int qExec(QObject *testObject, int argc = 0, char *argv = nullptr)执行在 testObject 中声明的测试。此外,如果存在私有槽 initTestCase()、cleanupTestCase()、init() 和 cleanup(),则会执行它们。以下示例将在 MyTestObject 中运行所有测试:MyTestObject test1;QTest::qExec(&test1);如果没有测试失败,则此函数返回 0,如果一个或多个测试失败或出现未处理的异常,则返回 0 以外的值。对于独立的测试应用程序,宏 QTEST_MAIN() 可用于声明解析命令行参数并执行测试的 main() 函数,避免显式调用此函数的需要。当使用 QTEST_MAIN() 宏时,此函数的返回值也是测试应用程序的退出代码。注意:此函数不可重入,一次只能运行一个测试。使用 qExec() 执行的测试不能通过 qExec() 运行另一个测试,并且不允许线程同时调用 qExec()。如果以编程方式创建了参数,而不是从 main() 中的参数获取它们,那么使用 qExec(QObject , const QStringList &) 会更安全。int qExec(QObject testObject, const QStringList &arguments)行为与 qExec(QObject , int, char) 相同,但采用 QStringList 参数而不是 char
21、
QSharedPointer qExtractTestData(const QString &dirName)
从资源中提取目录到磁盘。内容被递归提取到一个临时文件夹。一旦对返回值的最后引用超出范围,提取的内容将自动删除。
dirName 是要从资源中提取的目录的名称。返回提取数据的临时目录。
22、
void qSleep(int ms)
休眠 ms 毫秒,阻止执行测试。 qSleep() 不会做任何事件处理并且让测试没有响应。睡眠时网络通信可能会超时。 可以使用 qWait() 进行非阻塞休眠。
ms 必须大于 0。
qSleep() 函数在 unix 上调用 nanosleep() 或在 windows 上调用 Sleep(),因此 qSleep() 所用时间的准确性取决于操作系统。
23、
void qWait(int ms)
等待 ms 毫秒。在等待期间,将处理事件,并且测试将保持对用户界面事件或网络通信的响应。
24、
template bool qWaitFor(Functor predicate, int timeout = 5000)
等待超时毫秒或直到 predicate 返回 true。返回值是 predicate 的返回值。
MyObject obj; obj.startup(); QTest::qWaitFor(& { return obj.isReady(); }, 3000);
上面的代码将等待对象准备就绪,最多三秒钟。
25、
bool qWaitForWindowActive(QWindow *window, int timeout = 5000)
bool qWaitForWindowActive(QWidget *widget, int timeout = 5000)
等待 timeout 毫秒或直到小部件的窗口处于活动状态。返回最终是否处于活动状态。
26、
bool qWaitForWindowExposed(QWindow *window, int timeout = 5000)
bool qWaitForWindowExposed(QWidget *widget, int timeout = 5000)
等待 timeout 毫秒或直到小部件的窗口被暴露。返回最终是否被暴露。
27、
char * toHexRepresentation(const char *ba, int length)
返回指向字符串的指针,该字符串表示为以空格分隔的十六进制字符序列。如果输入被认为太长,则将其截断并在末尾添加省略号。调用者拥有返回指针的所有权,并且必须确保稍后将其传递给 operator delete[]。
length 是字符串 ba 的长度。
28、
template char * toString(const T &value)
返回值的文本表示。QCOMPARE() 使用此函数在测试失败的情况下输出详细信息。
可以将此函数的特化或重载添加到您的测试中以启用详细输出。
应该在类型的命名空间中提供一个 toString() 函数,而不是专门化这个模板。
注意:toString() 的调用者必须使用 delete[] 删除返回的数据。自定义的实现应该返回一个使用 new[] 或 qstrdup() 创建的字符串。 最简单的方法是创建一个 QByteArray 或 QString 并在其上调用 QTest::toString():
namespace { char *toString(const MyPoint &point) { return QTest::toString("MyPoint(" + QByteArray::number(point.x()) + ", " + QByteArray::number(point.y()) + ')'); } }
此函数还有多个重载版本,略。
四、部分宏成员
1、QBENCHMARK
此宏用于测量测试中代码的性能。要进行性能测试的代码包含在此宏之后的代码块中。代码块里面的代码会多次执行并计算平均执行时间。
2、QBENCHMARK_ONCE
此宏用于通过运行一次来测量代码块的性能。要进行基准测试的代码包含在此宏之后的代码块中。
3、QCOMPARE(actual, expected)
此宏使用相等运算符将实际值与预期值进行比较。如果实际和预期匹配,则继续执行。 如果不是,则在测试日志中记录失败,并且测试函数返回而不尝试任何后续检查。
QCOMPARE(QString("hello").toUpper(), QString("HELLO"));
如果比较失败,QCOMPARE() 会尝试输出值的内容,因此可以从测试日志中看到比较失败的原因。
使用初始化列表需要定义一个辅助宏来防止预处理器将逗号解释为宏参数分隔符:
#define ARG(...) VA_ARGS
QCOMPARE(QFontDatabase::standardSizes(), ARG({8, 10, 12, 16, 20, 24}));
#undef ARG
对于自定义的类,可以使用 QTest::toString() 格式化值以输出到测试日志中。
4、QEXPECT_FAIL(dataIndex, comment, mode)
此宏将下一个 QCOMPARE() 或 QVERIFY() 标记为预期失败。将报告预期的失败,而不是向测试日志添加失败。
如果 QVERIFY() 或 QCOMPARE() 被标记为预期失败,但通过了,则会将意外通过 (XPASS) 写入测试日志。
dataIndex 描述了测试数据中预期失败的条目。如果所有条目都预期失败或不存在测试数据,则传递一个空字符串 ("")。
comment 将附加到预期失败的测试日志中。
mode 是一个 QTest::TestFailMode 并设置测试是否应该继续执行。无论是否发生预期的测试失败,都会应用该模式。
QEXPECT_FAIL("", "Will fix in the next release", Continue);
QCOMPARE(i, 42);
QCOMPARE(j, 43);
在上面的示例中,如果变量 i 不是 42,则会将预期失败写入测试输出。如果变量 i 是 42,则会写入意外通过。QEXPECT_FAIL() 对示例中的第二个 QCOMPARE() 语句没有影响。
5、QFAIL(message)
此宏可用于强制测试失败。测试停止执行,失败消息将附加到测试日志中。
6、QFETCH(type, name)
此宏在堆栈上创建一个名为 name 的局部变量,其类型为 type。名称和类型必须与测试数据表中的列匹配。这是断言,如果断言失败,测试将中止。
假设一个测试有以下数据:
void TestQString::toInt_data(){ QTest::addColumn<QString>("aString"); QTest::addColumn<int>("expected"); QTest::newRow("positive value") << "42" << 42; QTest::newRow("negative value") << "-42" << -42; QTest::newRow("zero") << "0" << 0; }
测试数据有两个元素,一个称为aString 的QString 和一个称为expected 的整数。要在实际测试中获取这些值:
void TestQString::toInt(){ QFETCH(QString, aString); QFETCH(int, expected); QCOMPARE(aString.toInt(), expected); }
aString 和 expected 是堆栈上的变量,用当前测试数据初始化。
7、QFETCH_GLOBAL(type, name)
此宏从全局数据表中的一行中获取一个名为 name 的变量,其类型为 type。名称和类型必须与全局数据表中的列匹配。 这是断言,如果断言失败,测试将中止。
假设一个测试有以下数据:
void TestQLocale::initTestCase_data(){ QTest::addColumn<QLocale>("locale"); QTest::newRow("C") << QLocale::c(); QTest::newRow("UKish") << QLocale("en_GB"); QTest::newRow("USAish") << QLocale(QLocale::English, QLocale::UnitedStates); } void TestQLocale::roundTripInt_data() { QTest::addColumn<int>("number"); QTest::newRow("zero") << 0; QTest::newRow("one") << 1; QTest::newRow("two") << 2; QTest::newRow("ten") << 10; }
这里在两个地方定义了测试数据,使用 QFETCH_GLOBAL() 从全局数据表中读取区域设置,使用 QFETCH() 从本地数据表中读取数字:
void TestQLocale::roundTripInt(){ QFETCH_GLOBAL(QLocale, locale); QFETCH(int, number); bool ok; QCOMPARE(locale.toInt(locale.toString(number), &ok), number); QVERIFY(ok); }
8、QSKIP(description)
如果从测试函数调用,QSKIP() 宏会停止执行测试,而不会将失败添加到测试日志。可以使用它来跳过在当前配置中没有意义的测试。例如,如果测试系统上没有安装所需的字体,则字体渲染测试可能会调用 QSKIP()。
description 附加到测试日志中,并应包含对无法执行测试的原因的解释。
9、QTEST(actual, testElement)
QTEST() 是 QCOMPARE() 的一个便利宏,它将实际值与测试数据中的元素 testElement 进行比较。如果没有这样的元素,则测试断言。
除此之外,QTEST() 的行为与 QCOMPARE() 完全相同。
QFETCH(QString, myString);QCOMPARE(QString("hello").toUpper(), myString);
等同于:
QTEST(QString("hello").toUpper(), "myString");
10、QTEST_APPLESS_MAIN(TestClass)
实现一个执行测试类 TestClass 中所有测试的 main() 函数。
行为类似于 QTEST_MAIN(),但不实例化 QApplication 对象。将此宏用于非常简单的独立非 GUI 测试。
11、QTEST_GUILESS_MAIN(TestClass)
实现一个 main() 函数,该函数实例化一个 QCoreApplication 对象和 TestClass 测试类,并按照定义的顺序执行所有测试。使用此宏构建独立的可执行文件。
行为类似于 QTEST_MAIN(),但实例化一个 QCoreApplication 而不是 QApplication 对象。 如果测试用例不需要 QApplication 提供的功能,但仍需要事件循环,请使用此宏。
12、QTEST_MAIN(TestClass)
实现一个 main() 函数,该函数实例化一个应用程序对象和 TestClass,并按照定义的顺序执行所有测试。使用此宏构建独立的可执行文件。
如果定义了 QT_WIDGETS_LIB,应用程序对象就是一个 QApplication,如果定义了QT_GUI_LIB,应用程序对象就是一个 QGuiApplication,否则就是一个 QCoreApplication。如果使用 qmake 并且配置包括 QT += 小部件,则 QT_WIDGETS_LIB 将自动定义。同样,如果使用 qmake 并且配置包含 QT += gui,那么 QT_GUI_LIB 将被自动定义。
13、QTRY_COMPARE(actual, expected)
通过调用 QTRY_COMPARE_WITH_TIMEOUT() 执行实际值和预期值的比较,超时时间为 5 秒。
14、QTRY_COMPARE_WITH_TIMEOUT(actual, expected, timeout)
此宏与 QCOMPARE() 类似,但重复执行实际值和预期值的比较,直到两个值相等或达到 timeout(以毫秒为单位)。在每次比较之间,将处理事件。 如果达到超时,则在测试日志中记录失败,并且不会进一步执行测试。
15、QTRY_VERIFY2(condition, message)
通过调用 QTRY_VERIFY2_WITH_TIMEOUT() 检查条件,超时为 5 秒。如果条件仍然为假,则输出消息。 该消息是一个纯 C 字符串。
16、QTRY_VERIFY(condition)
通过调用 QTRY_VERIFY_WITH_TIMEOUT() 来检查条件,超时为 5 秒。
17、QTRY_VERIFY2_WITH_TIMEOUT(condition, message, timeout)
此宏与 QTRY_VERIFY_WITH_TIMEOUT() 类似,只是它在指定超时(以毫秒为单位)后条件仍然为假时输出详细消息。该消息是一个纯 C 字符串。
18、QTRY_VERIFY_WITH_TIMEOUT(condition, timeout)
此宏类似于 QVERIFY(),但会反复检查条件,直到条件变为真或达到超时(以毫秒为单位)。 在每次评估之间,将处理事件。如果超时,则在测试日志中记录失败,并且不会进一步执行测试。
19、QVERIFY2(condition, message)
此宏的行为与 QVERIFY() 完全相同,只是它在条件为假时报告一条消息。该消息是一个纯 C 字符串。
20、QVERIFY(condition)
此宏检查条件是否为真。如果为真,则继续执行。 如果不是,则在测试日志中记录失败,并且不会进一步执行测试。
如果将附加信息放入测试失败报告中是实用且有价值的,则可以使用 QVERIFY2()。
21、QVERIFY_THROWS_EXCEPTION(exceptiontype, ...)
此宏执行可变参数中给出的表达式,并期望捕获从表达式中抛出的异常。
22、QVERIFY_THROWS_NO_EXCEPTION(...)
此宏执行在其可变参数中给出的表达式,并尝试捕获从表达式中抛出的任何异常。