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);
- 現在の値に指定の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へ追記する必要があります。
.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本柱でやっていく以外にないように思います。
コメントをどうぞ