基于Qt Test的单体测试方法(一)

1.1 单体测试简介

软件开发一般流程包括:需求理解—>概要设计—>详细设计—>编码—>单体测试—>结合测试。单体测试(Uint Test)是软件开发阶段的一个基本环节,一般由开发者完成。那么,什么是单体测试?顾名思义就是对单体进行测试。所谓单体,可以理解为一个函数,而测试就是验证此函数是否能达到预期结果。所以,简单说,单体测试就是测试函数是否实现了预期结果。
目前,单体测试框架有很多。比如,应用于C/C++的Google Test,CppTest;应用于Java的JUint。它们的框架函数都很丰富,可以从不同角度对函数测试,能满足大多数项目的需求。但是,如果项目是基于Qt开发的,再搭建一套Google Test环境可能会很麻烦[1]。而Qt本身也提供了一个单体测试框架—-Qt Test,应用它更方便、快捷。

1.2 Qt Test使用方法

如何应用Qt Test对一个函数进行测试?下面以一个简单的Qt Console应用程序来进行说明。编程环境为Windwos下Qt Creator,基于Qt 5.5.1。

1.2.1 测试需求

有这样一个代表书籍的类Quote[2],下面类的定义与原书中稍有不同。它有两个属性:编号和单价,两个方法:编号和总价。net_price()前的virtual关键字先不用关注。现在要测试net_price()是否实现了通过单价和数量计算总价的功能。

class Quote{
public:
    Quote(const string& bookNo, double salePrice):
    m_bookNo(bookNo)
    ,m_salePrice(salePrice)
    {}
    string isbn() const
    {
        return m_bookNo;
    }
    virtual double net_price(int quantity)
    {
        return m_salePrice*quantity;
    }
private:
    string m_bookNo;
protected:
    double m_salePrice;
};

1.2.2 编写测试类

首先,建立一个Qt Console应用程序,命名为UintTest,并向其中加入类Quote。接下来编写测试类和测试函数。测试类用于包含测试函数,测试函数中对被测函数net_price()进行验证。要注意的是,测试类要继承QObject并使用Q_OBJECT,因为测试函数都是以槽(slot)形式定义的;此外,要包含QTest头文件。这样,测试框架才能执行测试函数。
测试类如下:

class Test_Quote:public QObject
{
    Q_OBJECT
public:
    Test_Quote();
    ~Test_Quote();

private slots:
    void net_price();
};

1.2.3 编写测试函数

这里测试函数可以和被测函数同名,它是测试真正实行的地方。测试之前需要理清以下几点:

  1. 被测函数的输入输出
  2. 如何表示出期待结果和实际输出
  3. 比较期待结果和实际输出

对于第1点,被测函数输出很明确,是一个总价格,为double类型;输入一个是函数的形参即数量,另一个是Quote的成员m_salePrice即单价。至于m_bookNo,它对实际输出没有影响,可以不算作输入。
这样一来,第2点也就明确了:构造出Quote对象,调用net_price(int quantity)得到实际输出,而期待结果事先就可以计算出来。
在实际项目中,输入输出有时并不明朗。输入可能是其他模块提供的值,这就需要构造适当的桩函数来模拟输入。而输出也可能不容易表示。比如,被测函数没有返回值,它执行的结果就是从数据库取得了一条数据、显示了一个画面等等,这时就需要根据实际情况选择恰当方法了。
最后一点,Qt Test的测试框架中提供了很多比较方法,常用的有QCOMPARE(actual, expected),QTRY_VERIFY(condition),可以通过Qt帮助查询它们的用法。而这里比较的是double类型,可以用bool qFuzzyCompare(double p1, double p2)来比较。
测试函数如下:

void Test_Quote::net_price()
{
    Quote cppPrimer("0001",128.0);

    //total price if buy ten books.
    double result = 1280.0;
    double actual = cppPrimer.net_price(10);

    qFuzzyCompare(actual,result);
}

1.2.4 执行测试函数

执行测试函数需要用到QTest中的方法。在main函数中实例化Test_Quote,再调用int QTest::qExec(QObject * testObject, int argc = 0, char ** argv = 0)就可以了,如下代码所示。

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Test_Quote testQuote;
    QTest::qExec(&testQuote);
    return a.exec();
}

尤其注意的是,为了编译通过要对工程文件做一些配置,即在UintTest.pro加入这一句:

QT += testlib

得到的输出如下,它把测试类中的每个测试函数都执行了。

********* Start testing of Test_Quote *********
Config: Using QtTest library 5.5.1, Qt 5.5.1 (i386-little_endian-ilp32 shared (d
ynamic) debug build; by GCC 4.9.2)
PASS   : Test_Quote::initTestCase()
PASS   : Test_Quote::net_price()
PASS   : Test_Quote::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of Test_Quote *********

可能会疑惑的是,测试类中并没有定义initTestCase()cleanupTestCase(),这两个可以认为是框架函数,其中:

initTestCase() //在第一个测试函数执行之前被调用
cleanupTestCase() //在所有测试函数执行结束后被调用

此外还有两个,

init() //每个测试函数执行之前被调用
cleanup() //每个测试函数执行结束后被调用

如有需要,它们可以为测试类数据成员做初始化和清理等工作。
以上,就是应用Qt Test做单体测试的简单介绍。

参考资料

[1] Qt使用Google Test 单元测试. http://blog.csdn.net/taoerit/article/details/39533563
[2] 《C++ Primer》15.2 Defining Base and Derived Classes

你可能感兴趣的:(软件开发,单体测试,Qt-Test)