Qt 5单元测试框架

简述:

所谓单元测试,类似gtest,即写接口验证你的单元功能,属于白盒测试。

Trolltech 公司提供了QTestLib框架,是一种基于Qt 编写的程序或库单元测试工具。

QTestLib 提供了单元测试框架的基本功能,并提供了针对GUI 测试的扩展功能。


QTestLib 的特性
特性 详细描述 
轻量级  QTestlib 只包含 6000行代码和60个导出符号 
自包含 
对于非GUI测试,QTestlib 只需要Qt核心库的几个符号 
快速测试
QTestlib 不需要特殊的测试执行程序,不需要为测试而进行特殊的注册
数据驱动测试 
一个测试程序可以在不同的测试数据集上执行多次 
基本的 GUI 测试 
QTestlib 提供了模拟鼠标和键盘事件的功能 
IDE 友好 
QTestlib 的输出信息可以被 Visual Studio 和 KDevelop 解析
线程安全
错误报告是线程安全的、原子性的
类型安全 
对模板进行了扩展使用,防止由隐式类型转换引起的错误 
易扩展 
用户自定义类型可以容易地加入到测试数据和测试输出中

创建一个测试的步骤是,首先继承QObject 类并添加私有的槽。每个私有的槽就是一个测试函数,然后使用 QTest::qExec() 执行测试对象中所有测试函数。


使用QTestLib,将结果数据表加载后逐行与结果数据对比。QTestLib所有相关功能都在QTest命名空间下。


1)  在PRO文件中将testlib加入QT参数中。

2)  创建测试类:需要继承自QObject(因为要使用信号-槽)。

3)  创建测试条目:所有的private slots下函数都将作为测试条目自动测试,并需要一个_data()函数提供数据。

4)  创建测试数据:QTest::addColumn(),QTest::newRow()。

5)  读取测试数据:QFETCH()

6)  对比测试结果与预期值::QCOMPARE(),QVerify()等。

7)  启动测试:QTest::qExec()或直接调用QTEST_APPLESS_MAIN()、QTEST_MAIN()、QTEST_GUILESS_MAIN()宏。

8)  测试Case启动、结束事件:initTestCase(),cleanupTestCase()

9)  测试条目启动、结束事件:init(),cleanup()

10) 测试库:直接测试库。

11) 测试源文件:将cpp加入Test工程,测试。


系统:Windows 7

Qt构建套件:qt-opensource-windows-x86-mingw530-5.7.0.exe

Qt Creator版本:4.0.2


1、简单的Qt 单元测试


首先实现计算圆面积的类,然后编写代码检查该类是否完成了相应的功能。


1 > 建立单元测试框架


选择“文件”→“新建文件或项目”菜单项,出现“New Project”的对话框,选择“其他项目”→“Qt单元测试”菜单项,单击“Choose...”按钮继续。




为测试项目命名“ 名称 ”为“ AreaTest ”,按向导单击“ 下一步 ”按钮,出现“ Qt 单元测试-Modules”的对话框,选择项目需要包含的模块




单击“下一步”按钮,在如图“Qt 单元测试-Details”对话框中设置将要创建的测试类的基本信息。





2 > 计算圆面积具体实现

area.h

#ifndef AREA_H
#define AREA_H
#include <QObject>

class Area:public QObject
{
    Q_OBJECT

public:
    Area(){}
    ~Area(){}
    Area(const Area &area)
    {
        m_r = area.m_r;
    }
    Area(int r)
    {
        m_r=r;
    }
    double CountArea()
    {
        return  3.14*m_r*m_r;
    }
private:
    double m_r;
};

#endif // AREA_H


3 > 测试代码实现

tst_testarea.cpp

#include <QString>
#include <QtTest>
#include "area.h"

class TestArea : public QObject
{
    Q_OBJECT
    
public:
    TestArea();
    
private Q_SLOTS:
    void toAreaTest(); //测试函数
};

TestArea::TestArea()
{
}

void TestArea::toAreaTest()
{
    Area area(1);  //初始化半径为1
    /**************************
     * 使用QVERIFY()宏判断半径为1的面积是否为3.14,由于浮点数不能
     * 直接比较,所以判断绝对值之差小于0.0000001,则认为结果正确。
     * QVERIFY()宏用于检查表达式是否为真,若为真,则程序继续运行,
     * 若测试失败,程序运行终止,如果需要在测试失败时输出信息,
     * 则可使用QVERIFY2(condition, message),
     * QVERIFY2()在"condition"条件验证失败时输出信息"message"
    **************************/
    QVERIFY(qAbs(area.CountArea()-3.14)<0.0000001);
    QVERIFY2(true, "Failure");
}

QTEST_APPLESS_MAIN(TestArea)

#include "tst_testarea.moc"

4 > 运行结果


QTEST_APPLESS_MAIN(TestArea) 宏实际上是一个main()函数,其定义如下:

#define QTEST_APPLESS_MAIN(TestObject) \
int main(int argc, char *argv[]) \
{ \
    TestObject tc; \
    QTEST_SET_MAIN_SOURCE_PATH \
    return QTest::qExec(&tc, argc, argv); \
}


实例化一个TestArea对象,然后调用 QTest::qExec()执行私有槽标识的所有测试方法;QTest是个namespace。

由运行结果可知,测试方法为 toAreaTest() ,同时还调用了测试框架自带的initTestCase()cleanupTestCase()

在测试框架中,有四个私有槽函数是预定义用作初始化和结束清理工作的:

initTestCase():在第一个测试函数执行前被调用。不显式定义也会被调用。

cleanupTestCase():在最后一个测试函数执行后被调用。不显式定义也会被调用。

init():在每个测试函数执行前被调用。不显式定义时是不会执行的

cleanup():在每个测试函数执行后被调用。不显式定义时是不会执行的

另外,除了QTEST_APPLESS_MAIN() 宏外,测试框架还提供了两个类似的宏,QTEST_MAIN() QTEST_GUILESS_MAIN(),用法相同。


2、数据驱动测试


对多种边界数据进行测试,并逐项初始化,逐项完成测试。

QTest::addColumn() 函数建立要测试的数据列。

QTest::newRow()函数添加数据行。


例子1:测试字符串转换为全小写字符的功能。

1 > 建立单元测试框架

操作方法同上。
项目名称:TestQString
测试类名:TestQString
测试槽:testToLower
生成源文件:tst_testqstring.cpp

2 > 测试代码具体实现

tst_testqstring.cpp


#include <QString>
#include <QtTest>

class TestQString : public QObject
{
    Q_OBJECT
    
public:
    TestQString();
    
private Q_SLOTS:
    //每个private slot都是一个被QTest::qExec()自动调用的测试函数
    void testToLower();
    //testToLower_data() 用以提供测试数据。
    //初始化数据的函数名和测试函数名一样,但增加了后缀"_data()"
    void testToLower_data();
};

TestQString::TestQString()
{
}

void TestQString::testToLower()
{
    //获取测试数据
    QFETCH(QString,string);
    QFETCH(QString,result);
    //如果两个参数不同,则其值会分别显示出来
    //QCOMPARE(actual,expected)宏比较实际值和期望值。
    QCOMPARE(string.toLower(),result);
    QVERIFY2(true, "Failure");
}

void TestQString::testToLower_data()
{
    //添加测试列
    QTest::addColumn<QString>("string");
    QTest::addColumn<QString>("result");
    //添加测试数据
    QTest::newRow("lower")<<"hello"<<"hello";
    QTest::newRow("mixed")<<"heLLO"<<"hello";
    QTest::newRow("upper")<<"HELLO"<<"hello";
}
//生成能够独立运行的测试代码
QTEST_APPLESS_MAIN(TestQString)

#include "tst_testqstring.moc"

3 > 测试结果





例子2:测试计算圆面积的功能

1 > 建立单元测试框架

操作方法同上。

项目名称:AreaTest2

测试类名:TestArea

测试槽:toArea

生成源文件:tst_testarea.cpp


2 > 具体实现


area.h

#ifndef AREA_H
#define AREA_H
#include <QtCore>
#include <QObject>

class Area:public QObject
{
    Q_OBJECT

public:
    Area(){}
    ~Area(){}
    Area(const Area &area)
    {
        m_r = area.m_r;
    }
    Area(int r)
    {
        m_r=r;
    }
    double CountArea()
    {
        return  3.14*m_r*m_r;
    }
private:
    double m_r;
};
/*********************
 * Q_DECLARE_METATYPE()宏将Area定义为元类型,这样所有基于模板的函数都可以使用Area
 * 而QTest中用到了模板函数addColumn(),因此须使用该宏使模板函数可以识别Area类。
*********************/
Q_DECLARE_METATYPE(Area)

#endif // AREA_H

tst_testarea.cpp

#include <QString>
#include <QtTest>
#include "area.h"

class TestArea : public QObject
{
    Q_OBJECT
    
public:
    TestArea();
    
private Q_SLOTS:
    void toArea();      //测试函数名toArea()
    void toArea_data(); //初始化数据的函数名toArea_data()和测试函数名toArea()一样,但增加了后缀_data
};

TestArea::TestArea()
{
}

void TestArea::toArea()
{
    //QFETCH()宏获取测试数据
    QFETCH(Area,area);
    QFETCH(double,r);
    //QVERIFY()宏将根据数据的多少决定函数运行多少次
    QVERIFY(qAbs(area.CountArea()-r)<0.0000001);
    QVERIFY2(true, "Failure");
}

void TestArea::toArea_data()
{
    //定义测试数据列
    QTest::addColumn<Area>("area");
    QTest::addColumn<double>("r");
    //建立测试数据
    QTest::newRow("1")<<Area(1)<<3.14;
    QTest::newRow("2")<<Area(2)<<12.56;
    QTest::newRow("3")<<Area(3)<<28.26;
}

QTEST_APPLESS_MAIN(TestArea)

#include "tst_testarea.moc"

3 > 运行结果




3、简单性能测试


1 > 建立单元测试框架

操作方法同上。

项目名称:TestQString2

测试类名:TestQString2

测试槽:testBenchmark

生成源文件:tst_testqstring2.cpp


2 > 具体实现

tst_testqstring2.cpp

#include <QString>
#include <QtTest>

class TestQString2 : public QObject
{
    Q_OBJECT
    
public:
    TestQString2();
    
private Q_SLOTS:
    void testBenchmark();
};

TestQString2::TestQString2()
{
}

void TestQString2::testBenchmark()
{
    QString str("heLLO");
    // 要用来测试性能的代码
    QBENCHMARK
    {
        str.toLower();
    }
    QVERIFY2(true, "Failure");
}

QTEST_APPLESS_MAIN(TestQString2)

#include "tst_testqstring2.moc"

3 > 运行结果



其中,0.00068 msecs per iteration (total: 90, iterations: 131072),其含义是测试代码运行了131072次,总时间90毫秒,每次运行的平均时间为0.00068毫秒。


4、GUI 测试


可以使用QTest::keyClick(),QTest::keyPress(),QTest::keyRelease(),QTest::mouseClick(), QTest::mouseDClick(), QTest::mouseMove(),QTest::mousePress() 和QTest::mouseRelease()函数来模拟相应的GUI事件。

样例代码:

UintTest 密码:c6x5

相关文章
相关标签/搜索