What is it, naokirin?

Qtのテストのためのライブラリ 『QTestLib』 を使ってみた

久しぶりの更新です。
そうはいっても、友人知人のブログの更新頻度を見ているとそうでもない方なのかな?

更新頻度って人によってまちまちでブログの更新頻度って人それぞれですよね。


それはさておき『QTestLib』について。

しっかりしたことを書くというよりは、私が使ってみることが(なんとか)できたのでその辺りを中心に書きます。

ちゃんとした内容が知りたい方はチュートリアル的なものはこちらAPIに関してはこちらを参照するとよいかと思います。

さて、このQt用と言ってもいいテスティングフレームワークを使うきっかけは、Google TestとQtライブラリの競合でした。ビルドしようとすると「std::stringstreamが...」といった内容のエラーが30個近く出て対処できなかったため、「もしやQt用のテスティングフレームワークがあるのでは?」と調べたら案の定ありました。

そんな感じで見つけたQTestLibですが、Qtを使ってる人なら確実に使うべきかもしれませんね。
単体テストのための機能も用意されていますが、GUIイベントのシミュレートやベンチマークの計測も可能なようです(この辺はチュートリアルをちょっと読んだくらいなのでどの程度のものなのかはわかりませんが)。

今回は使えた部分として比較くらいなので、GUIイベントのシミュレートやベンチマークは今度できるようになったときにでも書ければ書きます。

#include <QString>

struct itemData
{
    itemData(int num, const QString str) : itemNumber(num), memo(str) {}

    int itemNumber;
    QString memo;
};

というクラスをテストしてみます。大したものではないですがデータ比較のサンプルなのでこれでもいいかなと思います。

#include <QtTest/QtTest>

class ItemDataTest : public QObject
{
    Q_OBJECT
private slots:
    void testItemData_data();
    void testItemData();
};

"_data"とついたメソッドが"_data"を除いた部分が同じ名前のテストメソッドのデータの定義を行う関数になります。

#include "ItemDataTest.h"

void ItemDataTest::testItemData_data()
{
    QTest::addColumn<int>("number");
    QTest::addColumn<QString>("memoData");
	
    QTest::newRow("number:0, memoData:") << 0 << QString("");
    QTest::newRow("name:1, memoData:memo") << 1 << QString("memo");
}

void ItemDataTest::testItemData()
{
    QFETCH(int, number);
    QFETCH(QString, memoData);

    itemData iData(number, memoData);
		
    QCOMPARE(iData.itemNumber, number);
    QCOMPARE(iData.memo, memoData);
}

#include <QtCore/QCoreApplication>

QTEST_MAIN(ItemDataTest);
#include "moc_ItemDataTest.cpp"

"_data"の付いたメソッドでテストの入力値や期待値を格納しておくことができるテーブルを使っています。
たとえば

QTest::addColumn<int>("number");

ではint型の"number"というテーブルの列を定義し、その列に格納するデータがint型であるとしています。次にこのテーブルにデータを格納していきます。

QTest::newRow("number:0, memoData:") << 0 << QString("");

この例では行として"number:0, memoData:"という行を作り、"number"の列に0、memoDataの列に""を入れています。

このデータテーブルを使うことで、重複したテストコードを書かずにテストが書けるようになっているようです。次に実際のテストメソッドでこのデータテーブルを使ってテストを書きます。

QFETCH(int, number);

これはint型のローカル変数numberを作っています。しかし、ただローカル変数を作っているのではなくテスト時に対応する名前のデータテーブルの列のデータを格納するようになっています。

QFETCHマクロで作った変数を使ってテストを行います。

QCOMPARE(iData->itemNumber, number);

QCOMPARE(actual, expected)マクロは引数のactualとexpectedを"=="で比較して、偽だった場合にはテスト失敗とするマクロ関数です。

これによってテスト用のクラスはできました。あとは自動的に引数に渡したテスト用クラスを使ってテストを行うmain()関数を生成するQTEST_MAINマクロの呼び出しとmocファイルをインクルードするとテストを行う実行ファイルができます。

実際に実行すると、たとえば失敗した場合は

********* Start testing of ItemDataTest *********
Config: Using QTest library 4.7.1, Qt 4.7.1
PASS   : ItemDataTest::initTestCase()
FAIL!  : ItemDataTest::testItemData(number:1, memoData:memo) Compared values are not the same
   Actual (iData.memo): memo
   Expected (QString("")):
.\ItemDataTest.cpp(27) : failure location
PASS   : ItemDataTest::cleanupTestCase()
Totals: 2 passed, 1 failed, 0 skipped
********* Finished testing of ItemDataTest *********

のようになり、また成功した場合は

********* Start testing of ItemDataTest *********
Config: Using QTest library 4.7.1, Qt 4.7.1
PASS   : ItemDataTest::initTestCase()
PASS   : ItemDataTest::testItemData()
PASS   : ItemDataTest::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped
********* Finished testing of ItemDataTest *********

のように表示されます。

実際にはQCOMPARE以外にもテスト用のマクロも他にも多数あります。まだ私は使ったことが無いものがほとんどなのでここでは触れませんが。

GUIイベントのシミュレートができるテストライブラリというのは初めてなので少々わくわくしています。ベンチマークテストももう少し実用的な範囲の処理で使うと面白そうです。