1、常用宏
QBENCHMARK 计算运行时间,多次运行测量
QBENCHMARK_ONCE 计算运行时间,单次运行测量
QCOMPARE 比较是否相等
QEXPECT_FAIL 将下一个QCOMPARE或QVERIFY标记为预期测试,如果预期测试没有通过,可以终止剩余当前单元测试【可设置测试终止或继续】
QFAIL 强制抛出错误并打印信息
QFETCH 创建临时变量,从当前数据表中读取数据
QFETCH_GLOBAL 创建全局变量,从全局数据表中读取数据,必须有initTestCase_data【?】
QFINDTESTDATA 从路径获取测试数据【?】
QSKIP 停止测试,但不会将失败添加到失败日志
QTEST 可替代QFETCH+QCOMPARE的便利写法,不需要QFETCH来生成变量
QTEST_MAIN 创建一个main函数,并实例化应用程序对象和测试用例对象,QT_WIDGETS_LIB->QApplication(Qt+=widgets),QT_GUI_LIB->QGuiApplication(Qt+=gui),其他->QCoreApplication
QTEST_APPLESS_MAIN 与QTEST_MAIN相似但不实例化QApplication
QTEST_GUILESS_MAIN 与QTEST_MAIN相似只实例化QCoreApplication
QTRY_COMPARE_WITH_TIMEOUT 与QCOMPARE相似,但重复执行两个值的比较,直到两个值相等或者达到超时时间
QTRY_COMPARE 执行QTRY_COMPARE_WITH_TIMEOUT,并将超时时间设置为5秒
QVERIFY 判断条件是否为true,如果为true则继续执行测试,否则记录失败日志并且不再执行单元测试
QVERIFY2 与QVERIFY相似,但可以自定义失败日志
QTRY_VERIFY_WITH_TIMEOUT 与QVERIFY相似,但但重复执行条件的判断,直到条件为true或者达到超时时间
QTRY_VERIFY 执行QTRY_VERIFY_WITH_TIMEOUT,并将超时时间设置为5秒
QTRY_VERIFY2_WITH_TIMEOUT 与QVERIFY2相似,但但重复执行条件的判断,直到条件为true或者达到超时时间
QTRY_VERIFY2 执行QTRY_VERIFY2_WITH_TIMEOUT,并将超时时间设置为5秒
QVERIFY_EXCEPTION_THROWN 执行表达式,并判断语句抛出的异常是否与预期异常相等
QWARN 主动添加警告日志(线程安全)
2、创建Qt自动化测试工程
2.1、新建工程,可选Qt单元测试和Auto Test Project,在高版本中已经没有Qt单元测试这项了,建议选Auto Test Project,两者差不多,只是生成的默认宏不一样
默认往下,测试框架有Qt Test、GTest、BoostTest、Catch2等常用主流单元测试框架,这里选Qt Test,GTest和Boost都需要安装对应的环境
2.2、框架生成的默认函数
void initTestCase();
void cleanupTestCase();
这两个是框架自动调用的,出此以外还有:
void init();
void cleanup();
test_case1函数是用户可自定义的测试函数
总结一下系统自动调用的函数:
void initTestCase(); //在第一个测试函数执行之前被调用的初始化函数
void cleanupTestCase(); //在最后一个测试函数执行完毕之后调用的清理操作
void init(); //每次执行测试函数之前调用的初始化函数
void cleanup(); //每次执行完测试函数之后调用的清理操作
需要注意的是:测试函数必须声明成槽函数,此类的槽函数会被主动调用执行
3、开始测试
3.1、测试一个DbConvert类
Github:https://github.com/judesmorning/DbConvert
主要有这么一个函数:
3.2、将待测试的类集成到单元测试工程中
注意.pro中也要添加进来
SOURCES += tst_mytest.cpp \
../converttool.cpp
HEADERS += \
../converttool.h
3.3、常规测试
创建单次测试的函数,主要用于验证Qt单元测试可用到的工具宏:
实现:
void MyTest::test_qtest()
{
//计算运行时间,多次运行测量
#if 0
QBENCHMARK(m_tool->calc(QString("0"), ConvertTool::CalType_dBm2mW));
#endif
//计算运行时间,单次运行测量
#if 0
QBENCHMARK_ONCE(m_tool->calc(QString("0"), ConvertTool::CalType_dBm2mW));
#endif
//比较是否相等
#if 0
QCOMPARE(m_tool->calc(QString("0"), ConvertTool::CalType_dBm2mW), QString("1"));
#endif
//将下1个QCOMPARE或QVERIFY标记为预期测试,如果预期测试没有通过,可以终止剩余当前单元测试【可设置测试终止或继续】
//第1个参数为空只打印第2个参数,否则打印异常位置
//第1个参数为Continue、Abort,表示如果不通过是否继续执行剩余用例
#if 0
QEXPECT_FAIL("", "Will fix in the next release", Continue);
QCOMPARE(m_tool->calc(QString("0"), ConvertTool::CalType_dBm2mW), QString("10"));
#endif
//强制抛出错误并打印信息
#if 0
if (5 != 4)
{
QFAIL("This test has not been ported to this platform yet.");
}
#endif
//创建临时变量,从当前数据表中读取数据
#if 0
//在data函数中创建
QTest::addColumn<int>("a");
QTest::addColumn<int>("b");
QTest::newRow("positive value") << 100 << 100;
//在测试函数中取出
QFETCH(int, a);
QFETCH(int, b);
QCOMPARE(a, b);
#endif
//创建全局变量,从全局数据表中读取数据,必须有initTestCase_data
#if 0
//QFETCH_GLOBAL?
#endif
//从路径获取测试数据
#if 0
MyXmlParser parser;
QString input = QFINDTESTDATA("testxml/simple1.xml");
QVERIFY(parser.parse(input));
#endif
//停止测试,但不会将失败添加到失败日志
#if 0
if (4 != 5)
{
QSKIP("This test requires the SQLITE database driver");
}
#endif
//可替代QFETCH+QCOMPARE的便利写法,不需要QFETCH来生成变量
#if 0
QTEST(QString("hello").toUpper(), "myString");
#endif
//与QCOMPARE相似,但重复执行两个值的比较,直到两个值相等或者达到超时时间
#if 0
QTRY_COMPARE_WITH_TIMEOUT(1, 1, 5);
#endif
//执行QTRY_COMPARE_WITH_TIMEOUT,并将超时时间设置为5秒
#if 0
QTRY_COMPARE(1, 1);
#endif
//判断条件是否为true,如果为true则继续执行测试,否则记录失败日志并且不再执行单元测试
#if 0
QVERIFY(1+1==2);
#endif
//与QVERIFY相似,但可以自定义失败日志
#if 0
QVERIFY2(1+1==2, "A breach in basic arithmetic occurred.");
#endif
//与QVERIFY相似,但重复执行条件的判断,直到条件为true或者达到超时时间
#if 0
QTRY_VERIFY_WITH_TIMEOUT(1+1==2, 5);
#endif
//执行QTRY_VERIFY_WITH_TIMEOUT,并将超时时间设置为5秒
#if 0
QTRY_VERIFY(1+1==2);
#endif
//与QVERIFY2相似,但重复执行条件的判断,直到条件为true或者达到超时时间
#if 0
QTRY_VERIFY2_WITH_TIMEOUT(3>2, "this is log msg", 5);
#endif
//执行QTRY_VERIFY2_WITH_TIMEOUT,并将超时时间设置为5秒
#if 0
QTRY_VERIFY2(3>2, "this is log msg");
#endif
//执行表达式,并判断语句抛出的异常是否与预期异常相等
#if 0
//QVERIFY_EXCEPTION_THROWN
#endif
//主动添加警告日志,线程安全
#if 0
QWARN("msg");
#endif
}
3.4、数据驱动测试
创建一个函数test_calc,并将此函数名后加“_data”作为新的函数,再创建一个待测类指针:
①、
根据前面的说明,我们可以在initTestCase中new出指针,在cleanupTestCase中delete指针
void MyTest::initTestCase()
{
m_tool = new ConvertTool(nullptr);
}
void MyTest::cleanupTestCase()
{
delete m_tool;
m_tool = nullptr;
}
②、
void MyTest::test_calc_data()
{
/*创建测试参数*/
QTest::addColumn<QString>("calStr"); //创建第1个入参集合
QTest::addColumn<ConvertTool::CalType>("type"); //创建第2个入参集合
QTest::addColumn<QString>("result"); //创建返回值集合
/*设计用例*/
QTest::newRow("ok_1") << "0" << ConvertTool::CalType_dBm2mW << "1";
QTest::newRow("ok_2") << "10" << ConvertTool::CalType_dBm2mW << "10";
QTest::newRow("ng_1") << "10" << ConvertTool::CalType_dBm2mW << "100";
}
③、
void MyTest::test_calc()
{
//取出测试数据
QFETCH(QString, calStr);
QFETCH(ConvertTool::CalType, type);
QFETCH(QString, result);
//测试
QCOMPARE(m_tool->calc(calStr, type), result);
}
即test_calc函数会实例化test_calc_data中的测试用例并执行测试
④、
可直接运行程序,在应用程序输出里看结果
为了直观,可在Test Results里运行程序,通过图形的形式展示
在Test Results里,也可将测试结果输出到文档中:
右键最上面的测试行,选择“save output to file",可将结果存成文档:
说明:
1.如果待测工程很大,建议在待测工程基础上增加一个pri和一个自动化测试的类,屏蔽main函数