Qt (10)  QTestLibで簡単なテストを行う 
		ご利用のブラウザは、JavaScript が無効 となっていませんか?コンテンツの一部が非表示 、あるいは、コメント、お問い合わせの投稿ができない 、検索ができない ことがあります。
簡単なテストターゲットクラスを作る
今回のために、簡単なテストターゲット用の独自のクラスを用意してみました。
[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); 
	int minus(int 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へ追記する必要があります。
.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  
	-functions  
	-o filename  
	-silent  
	-v1  
	-v2  
	-vs  
	-xml  
	-lightxml  
	-eventdelay ms  
	-keydelay ms  
	-mousedelay ms  
	-keyevent-verbose  
	-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本柱でやっていく以外にないように思います。
 
		
 
  
コメントをどうぞ