ホーム

OFF-SOFT.net

OFF-SOFT.net

ウェブやソフトウェアに関するサポート&情報サイトです。サイト構築からソフトウェアの作成、利用まであなたの助けになるかも・・・・しれません。たぶん・・。

Qt (10) QTestLibで簡単なテストを行う

公開日| 2009年06月27日 | コメントはまだありません。
概要 :
 Qtでは、CppUnitのようなQTestLibがあります。

 今回の記事では、QTestLib簡単なテスト実行プログラムを作成してみましょう。
(この記事は、関連記事サイトのチュートリアルの一部です。Qtの初心者向けプログラマーをターゲットに記載されています。)

関連記事: http://doc.trolltech.com/4.5.1/qtestlib-manual.html

ここで使用したサンプルソースコード:

簡単なテストターゲットクラスを作る
今回のために、簡単なテストターゲット用の独自のクラスを用意してみました。

[TestPlus.h]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CTestPlus
{
public :
	CTestPlus();
	virtual ~CTestPlus();
 
	//	calc method
	int plus(int val);
	int minus(int val);
 
	//	value method
	int value();
	void value(int val);
 
private :
	int m_value[3];
};

[TestPlus.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "TestPlus.h"
 
CTestPlus::CTestPlus()
{
	for(int ni=0;ni<3;ni++)
		m_value[ni]=0;
}
 
CTestPlus::~CTestPlus()
{
}
 
int CTestPlus::plus(int val)
{
	m_value[0]=m_value[2];
	m_value[1]=val;
	m_value[2]=m_value[0]+m_value[1];
	return value();
}
 
int CTestPlus::minus(int val)
{
	m_value[0]=m_value[2];
	m_value[1]=val;
	m_value[2]=m_value[0]-m_value[1];
	return value();
}
 
int CTestPlus::value()
{
	return m_value[2];
}
void CTestPlus::value(int val)
{
	m_value[2]=val;
}

すごく単純なクラスです。
テストを行いたいメソッドは、以下の2つのメソッドです。
  • int plus(int val);
    - 現在の値に指定のval値を足しこみます。
  • int minus(int val);
    - 現在の値に指定のval値を減らします。

どんな計算をしたかをスタック上でも確認できるように 内部の値は、配列で持っています。


では、テストプログラムを以下のようにしてみました。

[sample.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "TestPlus.h"
#include <QtTest/QtTest>
 
 
class TestCTestPlus: public QObject
{
	Q_OBJECT
private slots:
	void testMinus_data();
 
	void testPlus();
	void testMinus();
};
 
void TestCTestPlus::testPlus()
{
	CTestPlus sobj;
	QBENCHMARK {
		sobj.value(3);
		sobj.plus(4);
	}
	QCOMPARE(sobj.value(), int(7));
}
 
void TestCTestPlus::testMinus_data()
{
	QTest::addColumn<int>("value");
	QTest::addColumn<int>("minus");
	QTest::addColumn<int>("result");
 
	QTest::newRow("3-4")   << 3   << 4   << -1;
	QTest::newRow("-1+5")  << -1  << -5  << 4;
	QTest::newRow("-1+4")  << -1  << -4  << 5;	//	error
	QTest::newRow("4-3")   << 4   << 3   << 1;
}
 
void TestCTestPlus::testMinus()
{
 
	QFETCH(int, value);
	QFETCH(int, minus);
	QFETCH(int, result);
 
	CTestPlus sobj;
	sobj.value(value);
 
	QCOMPARE(sobj.minus(minus), result);
}
 
QTEST_MAIN(TestCTestPlus)
#include "sample.moc"

簡単に解説しておきます。

まず、最初、最後に決まりごととして以下の件があります。
5行目、テスト用クラスは、必ず、QObjectを継承します。
50行目、テスト用クラスは、必ず、QTEST_MAINマクロを使用してmainプログラムを作成します。
51行目、テスト用クラスは、必ず、自身のmocを最後にincludeします。

QTEST_MAINに関しては、独自に作成することもできます。ただ、上記のやり方を踏襲した方が簡単なので、そのように記述しています。

テスト用のメソッドは、private slotsで定義します。
名前は、何でも良いですが、わかりやすい名前が良いでしょう。
15行目からは、plusメソッドのテストを行っています。
18行目は、ベンチマークテストの設定例です。QBENCHMARK()マクロで指定した範囲の処理能力テストを行うことができます。 デフォルトのテスト動作は、処理時間です。処理時間以外にも、ベンチマークテストのパラメータを指定することで、CPU CLOCKなどの情報を 出力させることもできます。

22行目は、比較しています。
QCOMPAREマクロでは、第一引数と第二引数が一致していればテストOKとなります。
第一引数と第二引数には、比較演算子(==)が使えるものは、全て設定できます。
例えば、CTestPlusに
bool operator==( const CTestPlus& _Right ) const;
のようなメソッドをオーバーロードすれば、QCOMPAREマクロで、CTestPlusのオブジェクト同士をチェックすることもできます。


25行目は、テストメソッド(testMinus)のデータ設定メソッドです。
テスト用メソッド名_data()は、テスト用メソッドのためのデータ設定メソッドとして予約名となります。
データ設定は、以下の設定を行う必要があります。
  • QTest::addColumn<T>テンプレートを使って、テスト用メソッドで使用するパラメータの型と名前を定義します。
  • QTest::newRow(name)を使って、テスト用データのパターン名とパラメータを定義します。

37行目からが、先に設定されたデストパラメータを取り出して、テストを実施するメソッドになります。
QFETCHマクロで、順次、データ設定されているパラメータを型、名前を指定して取り出します。
44行目から、取り出したパラメータを使ってテストを実行しているところです。

先に設定されているテストパターン毎に、このメソッドがコールされますので、ここでテストパターンの数などを意識する必要はありません。


あまり独自で何か特別なことをやらない限りは、非常に簡単です。

テストを行いましょう

では、早速、このテストプログラムをコンパイルして実行してみましょう。

コンパイル時は、以下のようにコマンドパラメータでQTHELPを取り込むか、.proへ追記する必要があります。
> qmake "QT+=testlib"

.proへ追記する場合は、以下の1行を追加します。

QT += testlib


このテストプログラムを実行すると、以下のような結果が表示されます。
ちゃんと、33行目のテストパターンについては、"FAIL(失敗)"となっています。

>testnew.exe
********* Start testing of TestCTestPlus *********
Config: Using QTest library 4.5.1, Qt 4.5.1
PASS   : TestCTestPlus::initTestCase()
RESULT : TestCTestPlus::testPlus():
     0.000044 msec per iteration (total: 47, iterations: 1048576)
PASS   : TestCTestPlus::testPlus()
FAIL!  :  style="color: #444;">TestCTestPlus::testMinus(-1+4) Compared values are not the same
   Actual (sobj.minus(minus)): 3
   Expected (result): 5
.\sample.cpp(47) : failure location
PASS   : TestCTestPlus::cleanupTestCase()
Totals: 3 passed, 1 failed, 0 skipped
********* Finished testing of TestCTestPlus *********
 
>testnew.exe testPlus
********* Start testing of TestCTestPlus *********
Config: Using QTest library 4.5.1, Qt 4.5.1
PASS   : TestCTestPlus::initTestCase()
RESULT : TestCTestPlus::testPlus():
     0.000044 msec per iteration (total: 47, iterations: 1048576)
PASS   : TestCTestPlus::testPlus()
PASS   : TestCTestPlus::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped
********* Finished testing of TestCTestPlus *********
 
>testnew.exe testMinus:-1+4
********* Start testing of TestCTestPlus *********
Config: Using QTest library 4.5.1, Qt 4.5.1
PASS   : TestCTestPlus::initTestCase()
FAIL!  :  style="color: #444;">TestCTestPlus::testMinus(-1+4) Compared values are not the same
   Actual (sobj.minus(minus)): 3
   Expected (result): 5
.\sample.cpp(47) : failure location
PASS   : TestCTestPlus::cleanupTestCase()
Totals: 2 passed, 1 failed, 0 skipped
********* Finished testing of TestCTestPlus *********
 
>testnew.exe testMinus:-1+5
********* Start testing of TestCTestPlus *********
Config: Using QTest library 4.5.1, Qt 4.5.1
PASS   : TestCTestPlus::initTestCase()
PASS   : TestCTestPlus::testMinus()
PASS   : TestCTestPlus::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped
********* Finished testing of TestCTestPlus *********
 
>testnew.exe testPlus -tickcounter
********* Start testing of TestCTestPlus *********
Config: Using QTest library 4.5.1, Qt 4.5.1
PASS   : TestCTestPlus::initTestCase()
RESULT : TestCTestPlus::testPlus():
     664 ticks per iteration (total: 664, iterations: 1)
PASS   : TestCTestPlus::testPlus()
PASS   : TestCTestPlus::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped
********* Finished testing of TestCTestPlus *********

すごく簡単なのですが、QTestLibの主な機能が、ここに、(GUIのテスト部分がありませんが)ほとんど記載されています。
入力パターンを変えているのは、このテスト用実行プログラムには、様々なパラメータを指定することができるので、その一例として 上記のパターンでテストをしてみました。
(パラメータの詳細は、以降参照)


テストプログラムには、自動的に以下のパラメータを指定できるようになります。
testname [options] [testfunctions[:testdata]]...

[options]は、以降を参照のこと。

[testfunctions[:testdata]]
一部のテストメソッドのみ実行したい場合に指定します。下記の形式で指定します。
メソッド名:テストパターン名
※テストパターン名が入力されない場合は、指定されたメソッドの全てのパターンがテストされます。

  • -help
    outputs the possible command line arguments and give some useful help.
  • -functions
    outputs all test functions available in the test.
  • -o filename
    write output to the specified file, rather than to standard output
  • -silent
    silent output, only shows warnings, failures and minimal status messages
  • -v1
    verbose output; outputs information on entering and exiting test functions.
  • -v2
    extended verbose output; also outputs each QCOMPARE() and QVERIFY()
  • -vs
    outputs every signal that gets emitted
  • -xml
    outputs XML formatted results instead of plain text
  • -lightxml
    outputs results as a stream of XML tags
  • -eventdelay ms
    if no delay is specified for keyboard or mouse simulation (QTest::keyClick(), QTest::mouseClick() etc.), the value from this parameter (in milliseconds) is substituted.
  • -keydelay ms
    like -eventdelay, but only influences keyboard simulation and not mouse simulation.
  • -mousedelay ms
    like -eventdelay, but only influences mouse simulation and not keyboard simulation.
  • -keyevent-verbose
    output more verbose output for keyboard simulation
  • -maxwarnings numberBR sets the maximum number of warnings to output. 0 for unlimited, defaults to 2000.

また、ベンチマークテストでは、更に以下のようなパラメータを指定することができます。
  • Walltime (default) All platforms
  • CPU tick counter -tickcounter Windows, Mac OS X, Linux, many UNIX-like systems.
  • Valgrind/Callgrind -callgrind Linux (if installed)
  • Event Counter -eventcounter All platforms

簡単な画面テストの例
以下は、チュートリアルにある画面のテスト例です。
あえて、チュートリアルにあるものを解説いたしません。

[sample.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <QtGui>
#include <QtTest/QtTest>
 
class TestKeyType: public QObject
{
	Q_OBJECT
 
private slots:
	void testKeyInput();
 
};
 
void TestKeyType::testKeyInput()
{
	QLineEdit lineEdit;
	QTest::keyClicks(&lineEdit, "hello world");
	QCOMPARE(lineEdit.text(), QString("hello world"));
 
	QTest::keyClicks(&lineEdit, "hello worlD2");
	QCOMPARE(lineEdit.text(), QString("hello world2"));
}
 
QTEST_MAIN(TestKeyType)
#include "sample.moc"


[実行結果]
********* Start testing of TestKeyType *********
Config: Using QTest library 4.5.1, Qt 4.5.1
PASS   : TestKeyType::initTestCase()
FAIL!  :  style="color: #444;">TestKeyType::testKeyInput() Compared values are not the same
   Actual (lineEdit.text()): hello worldhello worlD2
   Expected (QString("hello world2")): hello world2
.\sample.cpp(20) : failure location
PASS   : TestKeyType::cleanupTestCase()
Totals: 2 passed, 1 failed, 0 skipped
********* Finished testing of TestKeyType *********

上記では、keyClicksを使用していますが、他にも以下のものが使えます。

  • QTest::keyClicks()
  • QTest::keyClick()
  • QTest::keyPress()
  • QTest::keyRelease()
  • QTest::mouseClick()
  • QTest::mouseDClick()
  • QTest::mouseMove()
  • QTest::mousePress()
  • QTest::mouseRelease()

[sample.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <QtGui>
#include <QtTest/QtTest>
 
class TestKeyType: public QObject
{
	Q_OBJECT
 
private slots:
	void testKeyInput_data();
	void testKeyInput();
};
 
void TestKeyType::testKeyInput_data()
{
	QTest::addColumn<QTestEventList>("events");
	QTest::addColumn<QString>("expected");
 
	QTestEventList list1;
	list1.addKeyClick('a');
	QTest::newRow("char") << list1 << "a";
 
	QTestEventList list2;
	list2.addKeyClick('a');
	list2.addKeyClick(Qt::Key_Backspace);
	list2.addKeyClick(Qt::Key_Backspace);
	list2.addKeyClick(Qt::Key_Backspace);
	list2.addKeyClick(Qt::Key_Backspace);
	QTest::newRow("there and back again") << list2 << "";
 
	QTestEventList list3;
	list3.addKeyClick('a');
	list3.addKeyClick('b');
	list3.addKeyClick('c');
	list3.addKeyClick('d');
	QTest::newRow("char") << list3 << "a";
 
}
 
void TestKeyType::testKeyInput()
{
	QFETCH(QTestEventList, events);
	QFETCH(QString, expected);
 
	QLineEdit lineEdit;
 
	events.simulate(&lineEdit);
 
	QCOMPARE(lineEdit.text(), expected);
}
 
 
QTEST_MAIN(TestKeyType)
#include "sample.moc"


[実行結果]
********* Start testing of TestKeyType *********
Config: Using QTest library 4.5.1, Qt 4.5.1
PASS   : TestKeyType::initTestCase()
FAIL!  :  style="color: #444;">TestKeyType::testKeyInput(char) Compared values are not the same
   Actual (lineEdit.text()): abcd
   Expected (expected): a
.\sample.cpp(48) : failure location
PASS   : TestKeyType::cleanupTestCase()
Totals: 2 passed, 1 failed, 0 skipped
********* Finished testing of TestKeyType *********


一応、一通りのテストに関する事項を記載してみました。CppUnitよりは、簡単に記述できると思います。
また、画面もある程度は、テストの自動化ができます。
アルゴリズムのテストや、処理能力のテストなどには、非常に便利だろうと思います。
ただ、画面のテストの自動化は、なかなか、難しいというのが、個人的な感想です。

ご意見などありましたら、コメントいただけますようお願い致します。

もっと、Qt関連について詳しく知りたい方は、以下の本なども良いと思います。
Qtに関する日本語の本が少ないですね。「入門書」は、さすがに、このページを読まれるくらいの方は不要だと思います。
やっぱり、本+ネット+試してみる!!の3本柱でやっていく以外にないように思います。


コメント

コメントをどうぞ







  • はてなブックマークへ追加する
  • Facebookでシェアする
  • twitter でつぶやく
  • Google Plusでシェアする
  • Pocketでシェアする
ページトップへ