天天看點

Qt 5單元測試架構1、簡單的Qt 單元測試2、資料驅動測試3、簡單性能測試4、GUI 測試

簡述:

所謂單元測試,類似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...”按鈕繼續。

Qt 5單元測試架構1、簡單的Qt 單元測試2、資料驅動測試3、簡單性能測試4、GUI 測試
為測試項目命名“ 名稱 ”為“ AreaTest ”,按向導單擊“ 下一步 ”按鈕,出現“ Qt 單元測試-Modules”的對話框,選擇項目需要包含的子產品。
Qt 5單元測試架構1、簡單的Qt 單元測試2、資料驅動測試3、簡單性能測試4、GUI 測試
單擊“下一步”按鈕,在如圖“Qt 單元測試-Details”對話框中設定将要建立的測試類的基本資訊。
Qt 5單元測試架構1、簡單的Qt 單元測試2、資料驅動測試3、簡單性能測試4、GUI 測試
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 > 運作結果
Qt 5單元測試架構1、簡單的Qt 單元測試2、資料驅動測試3、簡單性能測試4、GUI 測試
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 > 測試結果
Qt 5單元測試架構1、簡單的Qt 單元測試2、資料驅動測試3、簡單性能測試4、GUI 測試

例子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 > 運作結果
Qt 5單元測試架構1、簡單的Qt 單元測試2、資料驅動測試3、簡單性能測試4、GUI 測試

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 > 運作結果
Qt 5單元測試架構1、簡單的Qt 單元測試2、資料驅動測試3、簡單性能測試4、GUI 測試
其中,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

繼續閱讀