TestNG使う機会があったのだけど、今まで使ったことがなかった。ので、とりあえずドキュメントの http://testng.org/doc/documentation-main.html 読んでテキトーに訳した。誤字脱字とか表記揺れ(ファクトリだったりファクトリ"ー"だったりする)があるけどあまりチェックしてないです。
1 - Introduction
TestNGはテストに関する様々な要求をシンプルにするよう設計されたテストフレームワークです。ユニットテスト(他クラスと独立した単一クラスのテスト)から統合テスト(複数クラス、複数パッケージ、アプリケーションサーバなど複数の外部フレームワークからなるシステム全体のテスト)まで対象とします。
テストの記述は一般的に3ステップのプロセスになります。
- テストのロジックを記述してそのコードにTestNGのアノテーションを付与。
- テストに関する情報をtestng.xmlもしくはbuild.xmlに追加。
- TestNGを実行。
クイックスタートはWelcome pageにあります。
このドキュメントで使われる概念は以下となります。
- スイート(suite)は一つのXMLファイルで表現する。スイートは一つ以上のテストを持ち、
<suite>
タグで定義する。 - テスト(test)は
<test>
で表現し、一つ以上のTestNGクラスを持つ。 - TestNGのクラスはJavaのクラスで少なくとも一つ以上のTestNGアノテーションを持つ。
<class>
タグで表現し、一つ以上のテストメソッドを持つ。 - テストメソッド(test method)はソース内で
@Test
アノテーションを付与したJavaのメソッド。
TestNGのテストは@BeforeXXX
および@AfterXXX
アノテーションによる設定が可能で、なんらかのポイントの前後でJavaのロジックを実行可能です。それらのポイントは以下にリストを示します*1。
以降のマニュアルでは以下を解説します。
- 全アノテーションの簡潔な説明。TestNGが提供する各種機能を伝えるためのもので、詳細を知るには各アノテーションの個別のセクションを参照してください。
- testng.xml、シンタックス、このファイルで指定可能なこと、に関する説明。
- 各種機能の詳細なリストと、アノテーションとtestng.xmlの組み合わせ方。
2 - Annotations
TestNGで利用可能なアノテーションとその属性に概要を示します。
テストクラスに対するTestNGの設定内容 | |
---|---|
@BeforeSuite @AfterSuite @BeforeTest @AfterTest @BeforeGroups @AfterGroups @BeforeClass @AfterClass @BeforeMethod @AfterMethod |
@BeforeSuite: スイート実行時の全テストの前に実行される。 @AfterSuite: スイート実行時の全テストの後に実行される。 @BeforeTest: @AfterTest: @BeforeGroups: 設定メソッドを直前に実行するグループのリスト。このグループに含まれる最初のテストメソッドが実行される直前に、このメソッドが実行されることが保証される。 @AfterGroups: 設定メソッドを直後に実行するグループのリスト。このグループに含まれる最後のテストメソッドが実行される直後に、このメソッドが実行されることが保証される。 @BeforeClass: カレントクラスの最初のテストメソッドが実行される前に実行されるメソッド。 @AfterClass: カレントクラスのすべてのテストメソッドが実行された後に実行されるメソッド。 @BeforeMethod: 各テストメソッドの前に実行されるメソッド。 @AfterMethod: 各テストメソッドの後に実行されるメソッド。 |
alwaysRun | beforeメソッド(beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not beforeGroups)でtrueの場合、この設定メソッドは属するグループが何であるかに関わらず実行される。 afterメソッド(afterSuite, afterClass, ...)でtrueの場合、この設定メソッドはそれ以前に一つ以上のメソッドが失敗あるいはスキップされたとしても実行される。 |
dependsOnGroups | このメソッドが依存するグループのリスト |
dependsOnMethods | このメソッドが依存するメソッドのリスト |
enabled | このクラス・メソッドを有効化するかどうか。 |
groups | このクラス・メソッドが属するグループのリスト |
inheritGroups | trueの場合、このメソッドはクラスレベルの@Testアノテーションで指定するグループに属する。 |
@DataProvider | このアノテーションを使うことでテストメソッドに対するデータ提供用のメソッドとなる。このメソッドは戻り値Objectが必須。各Objectにはテストメソッドのパラメータリストを代入可能。@TestメソッドでこのDataProviderからデータを受け取るには、このアノテーションのnameを@TestのdataProviderに指定する必要がある。 |
---|---|
name | データプロパイダの名前. 指定しない場合、データプロバイダ名は自動的にメソッド名が設定される。 |
parallel | trueの場合、このデータプロバイダで生成されるテストはパラレルに実行される。デフォルト値はfalse |
@Factory | テストクラスとしてTestNGが使用するobjectを返すファクトリにするメソッド。メソッドの戻り値はObjectが必須。 |
---|---|
@Listeners | テストクラスにリスナを定義する。 |
---|---|
value | org.testng.ITestNGListenerを拡張するクラスの配列。 |
@Parameters | @Testメソッドに渡すパラメータを記述する。 |
---|---|
value | メソッドのパラメータを埋めるのに使う変数のリスト。 |
@Test | このクラスもしくはメソッドをテストとして扱う。 |
---|---|
alwaysRun | trueの場合、依存するメソッドが失敗する場合でもこのテストメソッドは常に実行される。 |
dataProvider | このテストメソッドで用いるデータプロバイダ名。 |
dataProviderClass | データプロバイダを参照するためのクラス。未指定の場合、データプロバイダは現在のテストメソッドのクラスあるいはベースクラスの値を参照する。この属性を指定する場合、指定クラスのデータプロバイダメソッドはstaticの必要がある。 |
dependsOnGroups | このメソッドが依存するグループのリスト。 |
dependsOnMethods | このメソッドが依存するメソッドのリスト。 |
description | メソッドの説明。 |
enabled | クラス・メソッドのメソッドを有効化するかどうか。*2 |
expectedExceptions | テストメソッドがスローを期待する例外のリスト。もし例外が発生しないかリストと一致しないものがスローさされる場合、テストは失敗となる。 |
groups | このクラス/メソッドが属するグループのリスト。 |
invocationCount | このメソッドが実行されるべき回数。 |
invocationTimeOut | このメソッドが取るべき実行回数中(all the invocationcounts)における累積実行時間のミリ秒の最大値。この属性はinvocationCount未指定の場合は無視される。 |
priority | このメソッドの優先度。低プライオリティが先にスケジュールされる。 |
successPercentage | このメソッドが期待される成功のパーセンテージ。 |
singleThreaded | trueの場合、このテストクラスの全メソッドが同一スレッドでの動作を保証される。そのテストがparallel="methods"*3で実行されていたとしても同様。この属性はクラスレベルでのみ使用可能でメソッドレベルで使用した場合は無視される。注意:この属性はsequential (現在非推奨)と以前は呼ばれていました。 |
timeOut | このテストの最大ミリ秒。 |
threadPoolSize | このメソッドで用いるスレッドプールのサイズ。invocationCountが指定される場合にはメソッドは複数スレッドから実行される可能性がある。注意:この属性はinvocationCountの未指定の場合には無視される。 |
3 - testng.xml
TestNGを実行する方法は複数存在します。
このセクションではtestng.xml
のフォーマットについて解説します。(antとコマンドラインについては後述)
testng.xml
の現行のDTDについては http://testng.org/testng-1.0.dtd を参照してください。(便宜上のHTMLバージョンをブラウザで参照可能です。)
以下はtestng.xml
の例です。
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="Suite1" verbose="1" > <test name="Nopackage" > <classes> <class name="NoPackageTest" /> </classes> </test> <test name="Regression1"> <classes> <class name="test.sample.ParameterSample"/> <class name="test.sample.ParameterTest"/> </classes> </test> </suite>
クラス名ではなくパッケージ名を指定可能です。
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="Suite1" verbose="1" > <test name="Regression1" > <packages> <package name="test.sample" /> </packages> </test> </suite>
この例では、TestNGはtest.sample
パッケージの全クラスを参照してTestNGアノテーションを持つクラスのみ処理します。
また、includeないしexcludeするグループやメソッドを指定可能です。
<test name="Regression1"> <groups> <run> <exclude name="brokenTests" /> <include name="checkinTests" /> </run> </groups> <classes> <class name="test.IndividualMethodsTest"> <methods> <include name="testMethod" /> </methods> </class> </classes> </test>
testng.xml
内に新規グループを定義可能です。また、属性に追加の詳細を指定可能で、例えば、テストをパラレル実行するかどうか・スレッド数・JUnitテストの実行など、があります。
デフォルトでは、TestNGはXMLファイル上の順序でテストを実行します。もしこのファイル内のクラスとメソッドを未指定の順序で実行したい場合、preserve-order
属性をfalse
にします。
<test name="Regression1" preserve-order="false"> <classes> <class name="test.Test1"> <methods> <include name="m1" /> <include name="m2" /> </methods> </class> <class name="test.Test2" /> </classes> </test>
機能の全種類についてはDTDを参照するか、ドキュメントをこのまま読み進めて下さい。
4 - Running TestNG
TestNGを実行する方法は複数存在します。
このセクションではコマンドラインからTestNGを実行する方法のみ解説します。他の方法についてはリンク先を参照してください。
クラスパスにTestNGがあるとの想定で、TestNGを実行する最も簡単な方法は以下となります。
java org.testng.TestNG testng1.xml [testng2.xml testng3.xml ...]
実行対象のTestNGスイートを記述したXMLファイルを少なくとも一つ指定する必要があります。任意で以下のコマンドラインスイッチが利用可能です。
オプション | 引数 | 説明 |
---|---|---|
-configfailurepolicy | skip|continue | @Before* 系メソッドが失敗する場合にスイート内の残存テストの実行を続行(continue )するかスキップ(skip )するか。デフォルトはskip |
-d | ディレクトリ | レポート生成先のディレクトリ(デフォルトはtest-output ) |
-dataproviderthreadcount | テストをパラレル実行時にデータプロバイダで使われるスレッドのデフォルト数。 | テストをパラレル実行時にデータプロバイダで使われるスレッドのデフォルト最大値を設定する。パラレルモードが選択された場合(例えば-parallel オプション)にだけ有効となる。スイートの定義でオーバーライドが可能。 |
-excludegroups | グループのカンマ区切りリスト | 実行からexcludeしたいグループのリスト。 |
-groups | グループのカンマ区切りリスト | 実行したいグループのリスト。(例:"windows,linux,regression" ) |
-listener | クラスパス上で参照可能なJavaクラスのカンマ区切りリスト | 自前のテストリスナーを指定。リスナクラスはorg.testng.ITestListenerの実装が必須。 |
-methods | 完全修飾クラス名とメソッドのカンマ区切りリスト。例:com.example.Foo.f1,com.example.Bar.f2 |
実行するメソッドを個々に指定。 |
-methodselectors | メソッドセレクタを定義するJavaクラスとメソッドプロパティのカンマ区切りリスト | コマンドライン上でメソッドセレクタを指定。例:com.example.Selector1:3,com.example.Selector2:2 |
-parallel | methods|tests|classes | 指定する場合、テスト実行時におけるパラレルスレッドの使用方法の決定に使われるデフォルトメカニズムを設定する。未指定の場合、デフォルトメカニズムはパラレルスレッドには全く使われない。スイート定義でオーバーライドが可能。 |
-reporter | カスタムレポートリスナー用の拡張定義 | -listener オプションと似ているが、レポーターインスタンスにはJavaBeansスタイルのプロパティ設定が可能な点が異なる。例:-reporter com.test.MyReporter:methodFilter=*insert*,enableFiltering=true You can have as many occurences of this option, one for each reporter that needs to be added. |
-sourcedir | ディレクトリのセミコロン区切りリスト | javadocアノテーションを付与したテストのソースコードがあるディレクトリ。このオプションはjavadocアノテーションを使用する場合にのみ必要。(例:"src/test" もしくは"src/test/org/testng/eclipse-plugin;src/test/org/testng/testng" ) |
-suitename | テストスイート用で使われるデフォルト名 | コマンドライン上で定義するテストスイートに名前を指定する。suite.xmlやソースコードに異なる名前を定義している場合、このオプションは無視される。スイート名に空白を含めるにはダブルクォートで囲む。例:"like this" |
-testclass | クラスパス上にあるクラスのカンマ区切りリスト | カンマ区切りのクラスファイルのリスト(例:"org.foo.Test1,org.foo.test2" ) |
-testjar | jarファイル | テストクラスの含まれるjarファイルを指定。jarのルートにtestng.xm がある場合、それが使われ、そうでない場合はjar内でテストクラスとみなせるものがすべてテストクラスとみなされる。 |
-testname | テストに使われるデフォルト名 | コマンドライン上で定義されるテストの名前を指定する。suite.xmlやソースコードで別のテスト名を指定する場合、このオプションは無視される。スイート名に空白を含めるにはダブルクォートで囲む。例:"like this" |
-testnames | テスト名のカンマ区切りリスト | このオプションのテスト名と一致する<test> タグ内で定義されているテストのみ実行する。 |
-testrunfactory | クラスパス上にあるJavaクラス | 自前のテストランナーを指定する。クラスはorg.testng.ITestRunnerFactoryを実装する必要がある。 |
-threadcount | テストのパラレル実行時に使われるスレッドのデフォルト数 | テストのパラレル実行時に使われるスレッドのデフォルト最大値を指定。パラレルモードが設定されている場合にだけ有効となる(例-parallelオプション付与時)。スイート定義でオーバーライドが可能 |
-xmlpathinjar | jar内のXMLファイルのパス | この属性にはテストjar内のXMLファイルのパスを指定する(例:"resources/testng.xml" )。デフォルトは"testng.xml" で、これはjarファイルのルートに"testng.xml" ファイルがあることを意味します。このオプションは-testjar オプションを指定しない限り無視されます。 |
これらのドキュメントは引数無しでTestNGを実行することで参照可能です。
コマンドラインの中身をテキストファイルに移すことも可能で、例えばc:\command.txt
に移し、パラメータ参照用にそのファイルを使うようTestNGに指定できます。
C:> more c:\command.txt -d test-output testng.xml C:> java org.testng.TestNG @c:\command.txt
また、TestNGは Java Virtual Machineのコマンドラインでプロパティを渡せます。
java -Dtestng.test.classpath="c:/build;c:/java/classes;" org.testng.TestNG testng.xml
システムプロパティ
プロパティ | 型 | 説明 |
---|---|---|
testng.test.classpath | テストクラスの含まれるディレクトリのセミコロン区切りリスト | このプロパティが設定されると、TestNGはテストクラスの参照にクラスパスではなくこのプロパティを使用する。XMLファイルでpackage タグを使用していてクラスパスに大量のクラスがあるが大半がテストクラスでは無い場合に便利。 |
例:
java org.testng.TestNG -groups windows,linux -testclass org.test.MyTest
Antタスクとtestng.xmlでより多くのパラメータ(引数指定、メソッドのinclude、など)でTestNGを実行可能なため、コマンドラインでの実行はTestNGの学習であるとか、試したいことをちょっと試す場合に留めるのが良いでしょう。
重要: testng.xmlを指定する場合、テスト対象を指定するコマンドラインフラグは無視されます。例外は-includedgroups
と-excludedgroups
で、これらはtestng.xml
のinclusions/exclusions
でオーバーライドされます。
5 - Test methods, Test classes and Test groups
5.1 - Test methods
テストメソッドには@Test
アノテーションを付与します。@Test
を付与したメソッドの戻り値は、testng.xml
でallow-return-values
にtrue
を指定しない限り、無視されます。
<suite allow-return-values="true"> or <test allow-return-values="true">
5.2 - Test groups
TestNGでは様々にグルーピングしたテストメソッドの実行が可能です。メソッドをグループに含めるだけでなく、他のグループを含むグループを設定可能です。その設定後、あるグループは除外しつつ特定のグループ(もしくは正規表現)はインクルードするような実行が可能です。これにより、一連のテスト群を区分けするような柔軟性を得られます。また、二つの異なるテストグループを連続実行する場合、再コンパイルは不要です。
testng.xml
で指定するグループは<test>
もしくは<suite>
タグ下のどちらかで指定します。<suite>
タグで指定するグループはその下のすべての<test>
タグに適用されます。タグのグループはそすべて集積される点に注意してください。たとえば<suite>
で"a"グループを指定して<test>
で"b"を指定すると、"a"と"b"の両方ともがインクルードされます。
良くある例としては、テストに少なくとも二つのカテゴリがある例です。
- チェックインテスト(Check-in tests)。新しいコードをサブミットする前に実行するべきテスト。一般的には高速で、基本的な機能が壊れていないことを確認する。
- 機能テスト(Functional tests)。ソフトウェアの全機能をカバーして少なくとも一日に一度実行するべきテスト。理想的には継続的に実行したいもの。
基本的には、チェックインテストは機能テストのサブセットです。TestNGではテストグループを使うことで非常に直感的にサブセットを指定できます。例えば、テストクラス全体を"functest"グループに含め、いくつかのテストメソッドは"checkintest"グループに含めます。
public class Test1 { @Test(groups = { "functest", "checkintest" }) public void testMethod1() { } @Test(groups = {"functest", "checkintest"} ) public void testMethod2() { } @Test(groups = { "functest" }) public void testMethod3() { } }
TestNGで実行するには以下のようにします。
<test name="Test1"> <groups> <run> <include name="functest"/> </run> </groups> <classes> <class name="example1.Test1"/> </classes> </test>
当該クラスのテストメソッドがすべて実行されますが、checkintest
ではtestMethod1()
とtestMethod2()
のみ実行されます。
別の例としては、正規表現があります。テストメソッドの幾つかはLinuxでは実行しないような場合、以下のようにします。
@Test public class Test1 { @Test(groups = { "windows.checkintest" }) public void testWindowsOnly() { } @Test(groups = {"linux.checkintest"} ) public void testLinuxOnly() { } @Test(groups = { "windows.functest" ) public void testWindowsToo() { } }
Windows用のメソッドのみ実行するにはtestng.xmlを以下のようにします。
<test name="Test1"> <groups> <run> <include name="windows.*"/> </run> </groups> <classes> <class name="example1.Test1"/> </classes> </test>
注意:TestNGは正規表現を使用しますが、wildmatsは未使用です。この違いに注意してください(例えば、"anything"は"."(ドット・アスタリスク)にはマッチしますが、""(アスタリスク)にはマッチしません)。
Method groups
個々のメソッドのincludeとexcludeが可能です。
<test name="Test1"> <classes> <class name="example1.Test1"> <methods> <include name=".*enabledTestMethod.*"/> <exclude name=".*brokenTestMethod.*"/> </methods> </class> </classes> </test>
再コンパイルなしに単一のメソッドを除外する場合には便利ですが推奨はしません。Javaのコードをリファクタリングしはじめるとテストフレームワークを壊す可能があるためです(タグ内の正規表現がメソッドにマッチしなくなる可能性がある)。
5.3 - Groups of groups
グループには他のグループを含められます。そうしたグループはメタグループ("MetaGroups")と呼びます。例として、"checkintest"と"functest"を含む"all"グループを定義したいとします。"functest"は"windows"と"linux"グループを含み、 "checkintest"は"windows"のみ含みます。これは以下のようにプロパティファイルで定義します。
<test name="Regression1"> <groups> <define name="functest"> <include name="windows"/> <include name="linux"/> </define> <define name="all"> <include name="functest"/> <include name="checkintest"/> </define> <run> <include name="all"/> </run> </groups> <classes> <class name="test.sample.Test1"/> </classes> </test>
5.4 - Exclusion groups
TestNGはグループのincludeおよびexcludeが可能です。
例えば、最新の変更により一時的にテストが壊れることはしばしば発生し、修正にそれほど時間はかからない、とします。しかし、機能テストのクリーン実行を走らせたい場合、対象のテストを無効化し、後で復活させることを覚えておく必要があります。
この問題の最もシンプルな解決策は"broken"グループを作成して対象のテストをこのグループに含めます。上記の例で、testMethod2()が壊れたのでこれを無効化したいとします。
@Test(groups = {"checkintest", "broken"} ) public void testMethod2() { }
実行時にこのグループをexcludeするには以下のようにします。
<test name="Simple example"> <groups> <run> <include name="checkintest"/> <exclude name="broken"/> </run> </groups> <classes> <class name="example1.Test1"/> </classes> </test>
これにより、どのテストが壊れているかを明記した状態でテストのクリーン実行ができるので、後でこれを修正します。
注意:@Testおよび@Before/Afterアノテーションの"enabled"プロパティを使用してテストを無効化することもできます。
5.5 - Partial groups
クラスレベルでグループを定義してからメソッドレベルでもグループを追加できます。
@Test(groups = { "checkin-test" }) public class All { @Test(groups = { "func-test" ) public void method1() { ... } public void method2() { ... } }
上記のクラスでは、method2はクラスレベルで定義した"checkin-test"グループの一部ですが、method1()は"checkin-test"と"func-test"グループ両方に含まれます。
5.6 - Parameters
テストメソッドは引数無しである必要はありません。個々のテストメソッドで任意の数の引数を使用可能で、TestNGには@Parameters
アノテーションで引数を渡すことを指示します。
引数を渡すにはtestng.xml
かコードで行うかの2通りあります。
5.6.1 - Parameters from testng.xml
引数に単純な値を使う場合にはtestng.xml
で定義します。
@Parameters({ "first-name" }) @Test public void testSingleString(String firstName) { System.out.println("Invoked testString " + firstName); assert "Cedric".equals(firstName); }
このコードでは、JavaのメソッドのfirstName
引数にはXMLパラメータfirst-name
の値を渡すよう定義しています。XMLパラメータはtestng.xml
で定義します。
<suite name="My suite"> <parameter name="first-name" value="Cedric"/> <test name="Simple example"> <-- ... -->
同様な方法が@Before/After
と@Factory
でも使えます。
@Parameters({ "datasource", "jdbcDriver" }) @BeforeMethod public void beforeTest(String ds, String driver) { m_dataSource = ...; // look up the value of datasource m_jdbcDriver = driver; }
この場合、二つのメソッド引数ds
とdriver
はプロパティdatasource
とjdbc-driver
でそれぞれ与えます。
引数はOptionalアノテーション付きで宣言することも可能です。
@Parameters("db") @Test public void testNonExistentParameter(@Optional("mysql") String db) { ... }
"db"という名前のパラメータがtestng.xml
で見つからない場合、テストメソッドは@Optional
アノテーションで定義したデフォルト値"mysql"
を受け取ります。
@Parameters
は以下の場合に使用可能です。
@Test
,@Before/After
,@Factory
アノテーションを持つ任意のメソッド。テストクラスの最大1つまでのコンストラクタ。この場合、TestNGは
testng.xml
で定義した値で初期化した引数で指定のコンストラクタを、テストクラスの初期化が必要になる都度、実行します。この機能はテストメソッドで使用するフィールドを初期化するのに使います。注意: XMLパラメータはアノテーションと同じ順序でマッピングします。TestNGは引数の数が合わない場合にはエラーを報告します。 パラメータはスコープを持ちます。
testng.xml
では、<suite>
もしくは<test>
下で宣言出来ます。二つのパラメータが同じ名前の場合、<test>
で定義したものが優先します。すべてのテストで使用するパラメータを定義して、ある特定のテストではオーバーライドする必要がある場合には便利です。
5.6.2 - Parameters with DataProviders
testng.xml
でパラメータ定義するのは、複雑なパラメータを渡す場合やJavaで生成する必要のあるパラメータ(複雑なオブジェクトやプロパティファイル・DBなどから読み込むオブジェクト)の場合、あまり効果的ではありません。その場合には、テストに必要な値を作るためのデータプロバイダ(Data Provider)を使います。データプロパイダはオブジェクトの配列を返すメソッドです。このメソッドには@DataProvider
アノテーションを付与します。
// このメソッドはデータプロバイダ"test1"を宣言するテストメソッドにデータを // 提供します。 @DataProvider(name = "test1") public Object[][] createData1() { return new Object[][] { { "Cedric", new Integer(36) }, { "Anne", new Integer(37)}, }; } // このテストメソッドはデータプロバイダ"test1"が生成するデータを // 宣言しています。 @Test(dataProvider = "test1") public void verifyData1(String n1, Integer n2) { System.out.println(n1 + " " + n2); }
実行結果は以下のようになります。
Cedric 36 Anne 37
@Test
メソッドはdataProvider
属性でデータプロバイダを定義します。その名前は同一クラス内の@DataProvider(name="...")
とマッチするメソッドである必要があります。
デフォルトでは、データプロバイダは現在のテストクラスもしくはベースクラスを参照します。異なるクラスでデータプロバイダを使いたい場合、staticメソッドにするか、引数なしコンストラクタのクラスを用意して、dataProviderClass
属性にそのクラスを指定します。
public class StaticProvider { @DataProvider(name = "create") public static Object[][] createData() { return new Object[][] { new Object[] { new Integer(42) } }; } } public class MyTest { @Test(dataProvider = "create", dataProviderClass = StaticProvider.class) public void test(Integer n) { // ... } }
データプロバイダはインジェクションもサポートします。TestNGはインジェクション用のテストコンテキストを使います。データプロバイダのメソッドは以下二種類のいずれかを戻せます。
- オブジェクトの二次元配列(
Object[][]
)。一次元のサイズはテストメソッドの実行回数で、二次元のサイズはテストメソッドの引数型と互換性のあるオブジェクトの配列サイズです。上記のサンプルで示したようなキャストになります。 Iterator<Object[]>
。Object[][]
との違いはIterator
はテストデータの遅延作成が可能です。TestNGがイテレータを呼び出すと、イテレータが戻す引数でテストメソッドが一回ずつ実行されます。これが有用なのは、メソッドに渡す引数の組み合わせが大量にあるとか、事前にデータを作成したくない場合などです。
イテレータの例です。
@DataProvider(name = "test1") public Iterator<Object[]> createData() { return new MyIterator(DATA); }
最初の引数にjava.lang.reflect.Method
を取るような@DataProvider
を宣言する場合、TestNGはその最初の引数に現在のテストメソッドを渡します。複数のテストメソッドが同一の@DataProvider
を使用し、テストメソッドに応じて異なる値を返したい場合に有用です。
例として、以下のコードは@DataProvider
でテストメソッド名を表示します。
@DataProvider(name = "dp") public Object[][] createData(Method m) { System.out.println(m.getName()); // print test method name return new Object[][] { new Object[] { "Cedric" }}; } @Test(dataProvider = "dp") public void test1(String s) { } @Test(dataProvider = "dp") public void test2(String s) { }
以下のような表示になります。
test1 test2
データプロバイダはparallel
属性でパラレル実行が可能です。
@DataProvider(parallel = true) // ...
XMLファイルから実行するパラレルデータプロバイダは同一のスレッドプールを共有します。デフォルトではサイズは10です。XMLファイルの<suite>
で変更可能です。
<suite name="Suite1" data-provider-thread-count="20" > ...
異なるスレッドプールで少数の特定のデータプロバイダを実行したい場合、異なるXMLファイルで実行します。
5.6.3 - Parameters in reports
テストメソッド実行に使われたパラメータはTestNGが生成するHTMLレポートで参照できます。以下がその例です。
5.7 - Dependencies
場合によっては特定順序でテストメソッドを実行する必要があります。以下はその例です。
- あるテストメソッド実行前にいくつかのテストメソッドが正常に完了してから実行したい。
- 初期化メソッドもテストメソッドのように扱いたい場合(
@Before/After
のメソッドはレポートの対象にならない)*4
TestNGはアノテーションもしくはXMLで依存関係(dependencies)を定義可能です。
5.7.1 - Dependencies with annotations
@Test
アノテーションdependsOnMethods
もしくはdependsOnGroups
属性を使用可能です。
依存関係には二種類あります。
- Hard dependencies. 依存するすべてのメソッドの正常終了が必須。依存関係で少なくとも一つが失敗した場合、実行されずにレポートでスキップ(SKIP)にされます。
- Soft dependencies. メソッドが失敗したかどうかに関わらず、依存するメソッドの後に実行されます。ある順序でテストメソッドを実行したいが、そのテストメソッドの正常実行が依存メソッドの正常終了に依存しない場合に有用です。ソフトな依存関係は
@Test
に"alwaysRun=true"
を追加します。
以下はハードな依存関係の例です。
@Test public void serverStartedOk() {} @Test(dependsOnMethods = { "serverStartedOk" }) public void method1() {}
この例では、method1
はserverStartedOk()
に依存すると宣言されています。これにより、serverStartedOk()
が常に先に呼び出されます。
グループ全体に依存するメソッドも可能です。
@Test(groups = { "init" }) public void serverStartedOk() {} @Test(groups = { "init" }) public void initEnvironment() {} @Test(dependsOnGroups = { "init.*" }) public void method1() {}
この例では、method1
が正規表現"init.*",
にマッチするグループに依存すると宣言されています。これにより、method1
の前にserverStartedOk()
とinitEnvironment()
が常に呼び出されることが保証されます。
注意:前述のように、同一グループに属するメソッドの実行順序は保証されません。
もし失敗に依存するあるメソッドがハードな依存関係(デフォルトのalwaysRun=false
)の場合、そのメソッドに依存するメソッドはFAIL
ではなくSKIP
と記録されます。スキップされたメソッドは最終的なレポートでも同様に報告されます(HTMLでは赤でも緑でもない色)。スキップされたメソッドは必ずしも失敗したわけではない点に注意が必要です。
dependsOnGroups
とdependsOnMethods
の両方ともパラメータとして正規表現が可能です。dependsOnMethods
では、複数のオーバーロードされたメソッドがありうる場合、すべてのオーバーロードされたメソッドが呼ばれます。オーバーロードされたメソッドの一つのみ実行したい場合、dependsOnGroups
を使います。
依存メソッドのより詳細なサンプルについては、この記事を参照してください。この記事では複数の依存関係の問題に対する優れた解決策として継承を使用しています。
デフォルトでは、依存されるメソッドはクラスごとにグループ化されます。例えば、メソッドb()
がa()
に依存していてクラスのインスタンスが複数ある場合(データプロバイダのファクトリなどが起因で)、呼び出し順序は以下のようになります。
a(1) a(2) b(2) b(2)
TestNGはすべてのインスタンスがa()
メソッドを呼び出すまでb()
を実行しません。
この振る舞いは、webブラウザで様々な国コードでサインイン・サインアウトをテストする場合など、場合によっては望ましいものではありません。その場合、以下のような順序が望ましいと思われます。
signIn("us") signOut("us") signIn("uk") signOut("uk")
この順序にするためには、XML属性group-by-instances
を使います。この属性は<suite>
もしくは<test>
のどちらかで有効です。
<suite name="Factory" group-by-instances="true"> or <test name="Factory" group-by-instances="true">
5.7.2 - Dependencies in XML
testng.xml
でグループの依存関係を指定することも可能です。設定には<dependencies>
タグを使います。
<test name="My suite"> <groups> <dependencies> <group name="c" depends-on="a b" /> <group name="z" depends-on="c" /> </dependencies> </groups> </test>
<depends-on>
属性はスペース区切りのグループのリストを取ります。
5.8 - Factories
ファクトリーによりテストを動的に生成できます。例えば、webサイトのページに複数回アクセスするテストメソッドを作成するとして、実行ごとに異なる回数にしたいとします。
public class TestWebServer { @Test(parameters = { "number-of-times" }) public void accessPage(int numberOfTimes) { while (numberOfTimes-- > 0) { // access the web page } } }
<test name="T1"> <parameter name="number-of-times" value="10"/> <class name= "TestWebServer" /> </test> <test name="T2"> <parameter name="number-of-times" value="20"/> <class name= "TestWebServer"/> </test> <test name="T3"> <parameter name="number-of-times" value="30"/> <class name= "TestWebServer"/> </test>
これは管理が大変面倒になるため、代わりにファクトリーを使います。
public class WebTestFactory { @Factory public Object[] createInstances() { Object[] result = new Object[10]; for (int i = 0; i < 10; i++) { result[i] = new WebTest(i * 10); } return result; } }
テストクラスは以下のようになります。
public class WebTest { private int m_numberOfTimes; public WebTest(int numberOfTimes) { m_numberOfTimes = numberOfTimes; } @Test public void testServer() { for (int i = 0; i < m_numberOfTimes; i++) { // access the web page } } }
testng.xml
にはファクトリーメソッドを持つクラスの参照のみ必要で、テストインスタンスそれ自体は実行時に生成されます。
<class name="WebTestFactory" />
ファクトリーメソッドは@Test
や@Before/After
などのような引数を取ることが可能で、戻り値はObject[]
が必須です。戻されるオブジェクトは任意のクラス(ファクトリークラスと同一クラスでなくても良い)かつTestNGアノテーションを持つ必要もありません(その場合にはTestNGが無視する)。
ファクトリーはデータプロバイダとの併用も可能で、通常のメソッドあるいはコンストラクタのどちらかに@Factory
を付与します。以下はコンストラクタファクトリーの例です。
@Factory(dataProvider = "dp") public FactoryDataProviderSampleTest(int n) { super(n); } @DataProvider static public Object[][] dp() { return new Object[][] { new Object[] { 41 }, new Object[] { 42 }, }; }
TestNGは二つのテストクラスを生成し、コンストラクタを41と42で呼び出します。
5.9 - Class level annotations
@Test
アノテーションはテストメソッドではなくクラスに付与可能です。
@Test public class Test1 { public void test1() { } public void test2() { } }
クラスレベルの@Test
アノテーションにより、すべてのpublicメソッドがアノテーションを付与していなくてもテストメソッドになります。特定の属性を追加したい場合にはメソッドに@Test
を重複して付与することも可能です。
@Test public class Test1 { public void test1() { } @Test(groups = "g1") public void test2() { } }
test1()
とtest2()
はテストメソッドですが、test2()
はグループ"g1"に属します。
5.10 - Parallelism and time-outs
幾つかの方法でTestNGにテストを異なるスレッドで実行するよう指定できます。
5.10.1 - Parallel suites
複数のスイートファイル(例:"java org.testng.TestNG testng1.xml testng2.xml"
)がありそれぞれのスイートを別々のスレッドで動かしたい場合に有用です。スレッドプールのサイズを指定するコマンドラインフラグは以下のようになります。
java org.testng.TestNG -suitethreadpoolsize 3 testng1.xml testng2.xml testng3.xml
対応するantタスクはsuitethreadpoolsize
になります。
5.10.2 - Parallel tests, classes and methods
<suite>
タグのparallel
属性は以下の値のうち一つを取ります。
<suite name="My suite" parallel="methods" thread-count="5">
<suite name="My suite" parallel="tests" thread-count="5">
<suite name="My suite" parallel="classes" thread-count="5">
<suite name="My suite" parallel="instances" thread-count="5">
- parallel="methods": TestNGは異なるスレッドですべてのテストメソッドを実行します。依存メソッドも同様に異なるスレッドで実行されますが、指定順序で実行されます。
- parallel="tests": TestNGは同一スレッドで同一
<test>
タグ内のすべてのメソッドを実行し、個々の<test>
タグは異なるスレッドで実行されます。同一<test>
内にスレッドセーフではないすべてのクラスをグループし、同一スレッドでそれらのクラスが動作することを保証できます。TestNGを使うことで、テストの実行に可能な限り多数のスレッドを使えます。 - parallel="classes": TestNGは同一スレッドで同一クラスのすべてのメソッドを実行します。個々のクラスは異なるスレッドで動作します。
- parallel="instances": TestNGは同一スレッドの同一インスタンスのすべてのメソッドを実行します。二つの異なるインスタンスの二つのメソッドは異なるスレッドで動作します。
thread-count
属性により実行時にアロケートするスレッド数を指定可能です。
注意:@Test
のtimeOut
属性はparallel
とnon-parallel
の両方で動作します。
@Test
メソッドを異なるスレッドで実行するような指定も可能です。そのためにはthreadPoolSize
属性を使います。
@Test(threadPoolSize = 3, invocationCount = 10, timeOut = 10000) public void testServer() {
この例では、関数testServer
は3つの異なるスレッドから10回実行されます。また、タイムアウト10秒指定によりスレッドを永久にブロックしないことを保証します。
5.11 - Rerunning failed tests
スイート内で失敗するテストがある場合、TestNGは出力ディレクトリにtestng-failed.xml
ファイルを生成します。このXMLファイルには失敗したメソッドを再実行するのに必要な情報が含まれており、テスト全体を実行することなく失敗したテストを素早く再実行できます。
java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs testng.xml java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs test-outputs\testng-failed.xml
testng-failed.xml
は必要な依存メソッドを含むため、SKIPを除く失敗したメソッド実行が保証されます*5。
5.12 - JUnit tests
TestNGはJUnit 3およびJUnit 4のテストの実行が可能です。クラスパスにJUnitのjarファイルを配置し、testng.classNames
プロパティにJUnitのテストクラスを指定してtestng.junit
をtrue
にします。
<test name="Test1" junit="true"> <classes> <!-- ... -->
この場合のTestNGの振る舞いはクラスパス上のJUnitのバージョンに依存します。
- JUnit 3:
- クラス内の
test*
ではじまるすべてのメソッドが実行される。 - テストクラスに
setUp()
メソッドがある場合、すべてのテストメソッドの前に実行される。 - テストクラスに
tearDown()
メソッドがある場合、すべてのテストメソッドの後に実行される。 - テストクラスが
suite()
メソッドを持つ場合、そのメソッドが返すすべてのテストが実行される。
- クラス内の
- JUnit 4;
- TestNGはテストの実行に
org.junit.runner.JUnitCore
を使用する。
- TestNGはテストの実行に
5.13 - Running TestNG programmatically
プログラム内から簡単にTestNGを実行できます。
TestListenerAdapter tla = new TestListenerAdapter(); TestNG testng = new TestNG(); testng.setTestClasses(new Class[] { Run2.class }); testng.addListener(tla); testng.run();
このサンプルではTestNGオブジェクトを生成してテストクラスRun2
を実行します。また、TestListener
を追加しています。アダプタークラスorg.testng.TestListenerAdapterもしくはorg.testng.ITestListenerを実装します。このインタフェースにはいくつかのコールバックメソッドが含まれており、テストの開始・正常終了・失敗などをトラッキングできます。
同様に、testng.xml
ファイルのTestNGを実行したり、仮想的なtestng.xml
ファイルを生成することも可能です。そのためにはまず、org.testng.xmlパッケージからXmlClassやXmlTestなど、XMLタグに対応するクラスを参照します。
例として、以下のような仮想ファイルを生成したいとします。
<suite name="TmpSuite" > <test name="TmpTest" > <classes> <class name="test.failures.Child" /> <classes> </test> </suite>
以下のようなコードになります。
XmlSuite suite = new XmlSuite(); suite.setName("TmpSuite"); XmlTest test = new XmlTest(suite); test.setName("TmpTest"); List<XmlClass> classes = new ArrayList<XmlClass>(); classes.add(new XmlClass("test.failures.Child")); test.setXmlClasses(classes) ;
上記のXmlSuite
をTestNGに渡します。
List<XmlSuite> suites = new ArrayList<XmlSuite>(); suites.add(suite); TestNG tng = new TestNG(); tng.setXmlSuites(suites); tng.run();
5.14 - BeanShell and advanced group selection
testng.xml
の<include>
と<exclude>
タグは場合によっては不十分なため、あるテストメソッドをテスト実行に含むか含めないかを決定するのにBeanShellを使えます。
<test>
タグ下で式を指定します。
<test name="BeanShell test"> <method-selectors> <method-selector> <script language="beanshell"><![CDATA[ groups.containsKey("test1") ]]></script> </method-selector> </method-selectors> <!-- ... -->
testng.xml
に<script>
がある場合、TestNGは現在の<test>
タグ内のグループとメソッドの<include>
と<exclude>
を無視します。指定したBeanShell式が、あるテストメソッドが含まれるかそうでないかを決定する、唯一の基準になります。
以下はBeanShellスクリプトに関する補足です。
- boolean戻り値が必須。この制約以外、任意の妥当なBeanShellコードが使用可能です(例えば、平日には
true
を返して週末はfalse
を返す。これにより、日付依存で異なるテスト実行が可能となる)。 - TestNGは簡易的に以下の変数を定義している。
java.lang.reflect.Method method
: 現在のテストメソッド。org.testng.ITestNGMethod testngMethod
: 現在のテストメソッドの説明。 **java.util.Map<String, String> groups
: 現在のテストメソッドが属するグループのmap。 CDATA
宣言で式を囲むことが可能。上記例のように予約されたXMLキャラクタのエスケープを省略できる。
5.15 - Annotation Transformers
TestNGは実行時にすべてのアノテーションの内容を修正可能です。ソースコードのアノテーションは基本的に問題無いが、ある少数の特定ケースではこれをオーバーライドしたい場合に有用です。
これを実行するにはAnnotation Transformerを使います。
Annotation Transformerは以下のインタフェースを実行するクラスです。
public interface IAnnotationTransformer { /** * テストクラスから読み込んだTestNGアノテーションを修正する機会を提供 * するためにTestNGがこのメソッドを呼び出します。ITestインタフェースの * セッターを呼ぶことで必要な値に変更します。 * 注意点:testClass, testConstructor, testMethodのうち一つだけが * 非nullになります。 * * @param annotation テストクラスから読み込まれたアノテーション。 * @param testClass クラスにアノテーションがある場合、この引数はそのクラスを表す(そうでない場合null) * @param testConstructor コンストラクタにアノテーションがある場合、この引数はそのコンストラクタを表す(そうでない場合null) * @param testMethod メソッドにアノテーションがある場合、この引数はそのメソッドを表す(そうでない場合null) */ public void transform(ITest annotation, Class testClass, Constructor testConstructor, Method testMethod); }
他すべてのTestNGリスナ同様に、コマンドラインもしくはantで指定可能です。
java org.testng.TestNG -listener MyTransformer testng.xml
コードでは以下のようにします。
TestNG tng = new TestNG(); tng.setAnnotationTransformer(new MyTransformer()); // ...
transform()
メソッド呼び出されるとき、TestNGがそれ以降の処理を進める前にITest test
引数のセッターで別の値を設定可能です。
例えば、以下はテストクラスのテストメソッドinvoke()
にだけinvocationCount
属性をオーバーライドする方法を示しています。
public class MyTransformer implements IAnnotationTransformer { public void transform(ITest annotation, Class testClass, Constructor testConstructor, Method testMethod) { if ("invoke".equals(testMethod.getName())) { annotation.setInvocationCount(5); } } }
IAnnotationTransformer
は@Test
アノテーションのみ修正可能です。その他のTestNGアノテーション(設定アノテーション、@Factory
, @DataProvider
)を修正したい場合、IAnnotationTransformer2
を使います。
5.16 - Method Interceptors
TestNGがテストメソッドの実行順序を算出すると、それらのメソッドは二つのグループに分類されます。
- 連続メソッド実行(Methods run sequentially): 依存関係を持つテストメソッドもしくは依存先。これらのメソッドは特定順序で実行される。
- 不特定順序メソッド実行(Methods run in no particular order): 前者以外のすべてのメソッド。これらのテストメソッドはランダム実行されて次回実行時には変化する可能性がある*6(デフォルトでは、TestNGはクラスごとにテストメソッドのグループ化を試行する)。
後者に属するメソッドの実行順序を制御したい場合、TestNGは以下のインタフェースを定義しています。
public interface IMethodInterceptor { List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context); }
引数に渡されるメソッドのリストは、任意の順序で実行が可能なメソッドです。intercept
メソッドはIMethodInstance
のリストを返すことを期待され、以下のいずれか一つとなります。
- 引数のリストを別の順序にする。
IMethodInstance
オブジェクトの縮小リスト(A smaller list of IMethodInstance objects.)。IMethodInstance
オブジェクトの拡大リスト(A bigger list of IMethodInstance objects.)。
インターセプター定義後、リスナとしてTestNGに渡します。
java -classpath "testng-jdk15.jar:test/build" org.testng.TestNG -listener test.methodinterceptors.NullMethodInterceptor -testclass test.methodinterceptors.FooTest
ant
のシンタックスについてはant documentationのlisteners
属性を参照してください。
例えば、以下のMethod Interceptorは"fast"グループに属するテストメソッドを先に実行するようにメソッドの並べ替えを行います。
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) { List<IMethodInstance> result = new ArrayList<IMethodInstance>(); for (IMethodInstance m : methods) { Test test = m.getMethod().getConstructorOrMethod().getAnnotation(Test.class); Set<String> groups = new HashSet<String>(); for (String group : test.groups()) { groups.add(group); } if (groups.contains("fast")) { result.add(0, m); } else { result.add(m); } } return result; }
5.17 - TestNG Listeners
TestNGの振る舞いを変更可能なインターフェースがいくつか存在します。これらのインタフェースはTestNGリスナ("TestNG Listeners")と呼びます。
- IAnnotationTransformer (doc, javadoc)
- IAnnotationTransformer2 (doc, javadoc)
- IHookable (doc, javadoc)
- IInvokedMethodListener (doc], javadoc)
- IMethodInterceptor (doc, javadoc)
- IReporter (doc, javadoc)
- ISuiteListener (doc, javadoc)
- ITestListener (doc, javadoc)
これらのインタフェースを実装する場合、以下の方法のいずれかを用いてTestNGに設定します。
- コマンドラインオプション-listener
ant testng.xml
ファイルの<listeners>
- テストクラスの
@Listeners
アノテーション ServiceLoader
5.17.1 - Specifying listeners with testng.xml or in Java
以下はtestng.xml
でのリスナの定義方法です。
<suite> <listeners> <listener class-name="com.example.MyListener" /> <listener class-name="com.example.MyMethodInterceptor" /> </listeners> ...
もしくはJava内でもリスナを定義できます。
@Listeners({ com.example.MyListener.class, com.example.MyMethodInterceptor.class }) public class MyTest { // ... }
@Listeners
アノテーションにはorg.testng.ITestNGListener
を拡張するクラスを指定可能です。ただし、IAnnotationTransformer
とIAnnotationTransformer2
は除外します。TestNGはアノテーションの書き換えに使用可能なため、処理のなるべく早期にリスナの存在をTestNGに知らせる必要があります。そのため、testng.xml
ファイルでリスナを定義する必要があります。
注意点として、@Listeners
アノテーションは、testng.xml
ファイルに定義したかのように、スイートファイル全体に適用されます。スコープを制限したい場合(例えば、現在のクラスでの実行に限定する)リスナ内のコードでテストメソッドを実行するかどうか判定するチェックを行うように出来ます。
- まず制限を定義するのに使うための新規のカスタムアノテーションを定義します。
@Retention(RetentionPolicy.RUNTIME) @Target ({ElementType.TYPE}) public @interface DisableListener {}
- リスナ内に以下のようなチェックを設けます。
public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) { ConstructorOrMethod consOrMethod =iInvokedMethod.getTestMethod().getConstructorOrMethod(); DisableListener disable = consOrMethod.getMethod().getDeclaringClass().getAnnotation(DisableListener.class); if (disable != null) { return; } //else resume your normal operations }
- 呼び出したくないリスナのあるテストクラスに上記のアノテーションを付与します。
5.17.2 - Specifying listeners with ServiceLoader
ServiceLoader経由でクラスパス上のインタフェース実装を指定するメカニズムをJDKは提供しています。
ServiceLoader
を使う場合、リスナと設定ファイルを持つjarファイルを作成し、TestNG実行時のクラスパスにそのjarファイルを配置するとTestNGは自動的にjarファイルを参照します。
以下にサンプルを示します。
まずリスナを作成します。
package test.tmp; public class TmpSuiteListener implements ISuiteListener { @Override public void onFinish(ISuite suite) { System.out.println("Finishing"); } @Override public void onStart(ISuite suite) { System.out.println("Starting"); } }
このファイルをコンパイルし、META-INF/services/org.testng.ITestNGListener
というディレクトリでファイルを作成します。このファイルにはITestNGListener
の実装を列挙します。
最終的に以下のようなディレクトリ構造になり、二つのファイルが含まれます。
$ tree |____META-INF | |____services | | |____org.testng.ITestNGListener |____test | |____tmp | | |____TmpSuiteListener.class $ cat META-INF/services/org.testng.ITestNGListener test.tmp.TmpSuiteListener
このディレクトリでjarファイルを作成します。
$ jar cvf ../sl.jar . added manifest ignoring entry META-INF/ adding: META-INF/services/(in = 0) (out= 0)(stored 0%) adding: META-INF/services/org.testng.ITestNGListener(in = 26) (out= 28)(deflated -7%) adding: test/(in = 0) (out= 0)(stored 0%) adding: test/tmp/(in = 0) (out= 0)(stored 0%) adding: test/tmp/TmpSuiteListener.class(in = 849) (out= 470)(deflated 44%)
次に、TestNG実行時にクラスパスに上記のjarを含めます。
$ java -classpath sl.jar:testng.jar org.testng.TestNG testng-single.yaml Starting f2 11 2 PASSED: f2("2") Finishing
このメカニズムを使うことで、testng.xml
でリスナを定義するよう個々の開発者に依頼する代わりに、クラスパスにjarを追加するだけでリスナを適用できます。
5.18 - Dependency injection
TestNGは二種類のDIをサポートしています。native(TestNG自体で実行)とexternal(GuiceなどDIフレームワークによる実行)
5.18.1 - Native dependency injection
TestNGではメソッドに引数を宣言可能です。その場合、TestNGは自動的にそれらの引数に値を与えます。DIは以下の箇所で使用可能です。
- 任意の@Beforeもしくは@Testメソッドで
ITestContext
型の引数を宣言可能。 - 任意の@AfterMethodで
ITestResult
型の引数を宣言可能。この引数はテストメソッド実行後の結果を反映したものとなる。 - 任意の@Beforeと@Afterメソッドで
XmlTest
型の引数を宣言可能。この引数は現在の<test>
タグを持つ。 - 任意の@BeforeMethod(と@AfterMethod)で
java.lang.reflect.Method
型の引数を宣言可能。この引数は@BeforeMethod終了後に呼ばれるテストメソッドが渡される(@AfterMethodの場合には実行したテストメソッド)。 - 任意の@BeforeMethodで
Object[]
型の引数を宣言可能。この引数はテストメソッドに渡されるであろう引数のリストが渡されます。そのテストメソッドはTestNGがインジェクト可能な、java.lang.reflect.Method
もしくは@DataProvider
由来のものです。 - 任意の@DataProviderで
ITestContext
もしくはjava.lang.reflect.Method
型の引数を宣言可能。後者の引数は呼び出されるテストメソッドが渡されます。
@NoInjection
アノテーションでDIをオフに出来ます。
public class NoInjectionTest { @DataProvider(name = "provider") public Object[][] provide() throws Exception { return new Object[][] { { CC.class.getMethod("f") } }; } @Test(dataProvider = "provider") public void withoutInjection(@NoInjection Method m) { Assert.assertEquals(m.getName(), "f"); } @Test(dataProvider = "provider") public void withInjection(Method m) { Assert.assertEquals(m.getName(), "withInjection"); } }
5.18.2 - Guice dependency injection
Guiceを使う場合、TestNGはGuiceモジュールを使用してテストオブジェクトをインジェクトする方法を用意しています。
@Guice(modules = GuiceExampleModule.class) public class GuiceTest extends SimpleBaseTest { @Inject ISingleton m_singleton; @Test public void singletonShouldWork() { m_singleton.doSomething(); } }
このサンプルでは、GuiceExampleModule
はISingleton
インタフェースに何らかの具象型にバインドされるよう指定しています。
public class GuiceExampleModule implements Module { @Override public void configure(Binder binder) { binder.bind(ISingleton.class).to(ExampleSingleton.class).in(Singleton.class); } }
テストクラスのインスタンス化に使うモジュール指定をより柔軟にしたい場合、モジュールファクトリーを使います。
@Guice(moduleFactory = ModuleFactory.class) public class GuiceModuleFactoryTest { @Inject ISingleton m_singleton; @Test public void singletonShouldWork() { m_singleton.doSomething(); } }
モジュールファクトリーはIModuleFactoryインタフェースを実装します。
public interface IModuleFactory { /** * @param context 現在のテストコンテキスト * @param testClass テストクラス * * @return このテストクラスのインスタンスを取得するのに使用する * Guiceモジュール。 */ Module createModule(ITestContext context, Class<?> testClass); }
ファクトリには、テストコンテキストのインスタンスと、TestNGがインスタンス化を担当するテストクラスが渡されます。createModule
メソッドはGuice
モジュールを戻す必要があり、このモジュールでは該当のテストクラスのインスタンス化を実装します。テストコンテキストからはtestng.xml
で指定するパラメータなどの環境情報が参照できます。parent-module
とguice-stage
スイートパラメータを使うことで、Guice
の機能とさらなる柔軟性が得られます。guice-stage
は親インジェクタ(parent injector)の生成で使用するStageを選択出来るようにします。デフォルトはDEVELOPMENT
です。他に選択可能な値としてはPRODUCTION
とTOOL
があります。test.xmlでparent-moduleを定義するには以下のようにします。
<suite parent-module="com.example.SuiteParenModule" guice-stage="PRODUCTION"> </suite>
TestNGは与えられたスイートごとに一つのモジュールを生成します。
テスト固有のGuiceモジュールとモジュールファクトリのインスタンス取得をこのモジュールで行うことも可能です。その場合、各テストケースごとに子インジェクタを生成します。その場合、すべての共通なバインディングをparent-module
で宣言すると、モジュールおよびモジュールファクトリのparent-module
で宣言したバインディングをテストクラスでインジェクト出来ます。以下がこの機能の例になります。
package com.example; public class ParentModule extends AbstractModule { @Override protected void conigure() { bind(MyService.class).toProvider(MyServiceProvider.class); bind(MyContext.class).to(MyContextImpl.class).in(Singleton.class); } }
package com.example; public class TestModule extends AbstractModule { private final MyContext myContext; @Inject TestModule(MyContext myContext) { this.myContext = myContext } @Override protected void configure() { bind(MySession.class).toInstance(myContext.getSession()); } }
<suite parent-module="com.example.ParentModule"> </suite>
package com.example; @Test @Guice(modules = TestModule.class) public class TestClass { @Inject MyService myService; @Inject MySession mySession; public void testServiceWithSession() { myService.serve(mySession); } }
上記の例ではParentModuleはMyServiceとMyContextクラスのバインディングを宣言しています。TestModuleクラスにコンストラクタインジェクションでMyContextがインジェクトされ、TestModuleはMySessionとのバインディングを宣言しています。test XMLファイルのparent-moduleにはParentModuleクラスが設定されており、この設定によりTestModuleへのインジェクションが可能となります。TestClassには二つのインジェクションがあり、MyServiceはParentModuleから、MySessionはTestModuleからバインドされます。この設定によってスイート内のすべてのテストが同一セッションインスタンスで動作することが保証されます。スイートごとに唯一のMyContextImplオブジェクトが生成されます。このインスタンスを通じてスイート内のすべてのテストに関する共通の環境情報を設定可能です。
5.19 - Listening to method invocations
IInvokedMethodListenerを使うことでTestNGがテスト(@Test
を付与したもの)呼び出しをしたり設定メソッド(@Before
や@After
なを付与したもの)実行時に通知を受けられます。以下のインターフェースを実装します。
public interface IInvokedMethodListener extends ITestNGListener { void beforeInvocation(IInvokedMethod method, ITestResult testResult); void afterInvocation(IInvokedMethod method, ITestResult testResult); }
リスナとして宣言する方法についてはTestNGリスナのセクションを参照してください。
5.20 - Overriding test methods
TestNGではテストメソッドのオーバーライドやスキップが可能です。これが役に立つ例としては、特定のセキュリティマネージャーでテストメソッドを動かしたい場合です。これにはIHookableを実装するリスナを作成します。
JAASの例は以下の通りです。
public class MyHook implements IHookable { public void run(final IHookCallBack icb, ITestResult testResult) { // Preferably initialized in a @Configuration method mySubject = authenticateWithJAAs(); Subject.doAs(mySubject, new PrivilegedExceptionAction() { public Object run() { icb.callback(testResult); } }; } }
5.21 - Altering suites (or) tests
スイートファイルの中身を変更せずに実行時にスイートXMLのテストタグもしくはスイートを変更したい場合があります。
A classic example for this would be to try and leverage your existing suite file and try using it for simulating a load test on your "Application under test".*7
TestNGではリスナ警手で実行時にスイートXMLファイル内のテストやスイートタグを変更可能です。これにはIAlterSuiteListenerを実装するリスナを作成します。リスナについてはリスナのセクションを参照してください。
以下に実行時にスイート名を変更する方法を示します。
public class AlterSuiteNameListener implements IAlterSuiteListener { @Override public void alter(List<XmlSuite> suites) { XmlSuite suite = suites.get(0); suite.setName(getClass().getSimpleName()); } }
以下の方法のどちらかでリスナを追加可能です。
- スイートXMLファイル内の
<listeners>
タグ経由。 - Service Loader経由
このリスナについては@Listeners
アノテーションを使用した追加は出来ません。
6 - Test results
6.1 - Success, failure and assert
テストは、例外をスローせずに完了するか、期待される例外がスローされるか(@Test
のexpectedExceptions
属性のドキュメントを参照)、であれば正常終了と見なします。
テストメソッドは基本的には、例外スロー可能な呼び出しにするか、各種のアサートで構成されます(Javaの"assert"キーワードの使用)。"assert"失敗時はAssertionErrorExceptionがスローされ、そのメソッドは失敗と見なされます(アサートエラーが不要な場合にはJVMの-eaオブションを使用します)。
以下はテストメソッドの例です。
@Test public void verifyLastName() { assert "Beust".equals(m_lastName) : "Expected name Beust, for" + m_lastName; }
TestNGではJUnitのAssertクラスもインクルードしています。複雑なオブジェクトでアサートを実行できます。
import static org.testng.AssertJUnit.*; //... @Test public void verify() { assertEquals("Beust", m_lastName); }
上記ではstaticインポートを使用してクラスのプレフィクスを無くしています。
6.2 - Logging and results
テスト実行の結果はSuiteRunnder起動時に指定するディレクトリにindex.html
ファイルが生成されます。このファイルは別の各種HTMLおよびテキストファイルを参照しており、それらの各種ファイルにテスト実行全体の結果が含まれます。一般的なサンプルはこちらから参照できます。
TestNGでは自前のレポートの生成はリスナおよびレポータ(Reporters)を使用して容易に行えます。
- リスナ(Listeners)はorg.testng.ITestListenerインタフェースを実装し、テストの開始・通過・失敗などの実時間で通知が行われる。
- レポータ(Reporters)はorg.testng.IReporterインタフェースを実装し、すべてのスイートがTestNGによって実行された時点で通知が行われる。IReporterインスタンスはテスト実行全体の情報を持つ多数のオブジェクトが渡されます。
例えば、テスト実行をPDFレポートで生成したい場合、テストの実時間の通知は必要無いのでIReporter
で十分と思われます。テストのリアルタイムレポート、たとえば、プログレスバーを持つGUIであるとか一つ一つのテスト実行ごとにドット(".")を表示するテキストレポーター、を構築したいのであれば、ITestListener
を実装します。
6.2.1 - Logging Listeners
以下は個々のテストがパスするたびに"."、失敗は"F"、スキップは"S"を表示するリスナです。
public class DotTestListener extends TestListenerAdapter { private int m_count = 0; @Override public void onTestFailure(ITestResult tr) { log("F"); } @Override public void onTestSkipped(ITestResult tr) { log("S"); } @Override public void onTestSuccess(ITestResult tr) { log("."); } private void log(String string) { System.out.print(string); if (++m_count % 40 == 0) { System.out.println(""); } } }
この例では、TestListenerAdapterを拡張しており、このアダプタはITestListenerを空メソッドで実装しているため、不要なその他のインタフェースのメソッドはオーバーライドしていません。必要に応じて直接インタフェースを実装することも可能です。
新規のリスナをTestNGで使うには以下のようにします。
java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -listener org.testng.reporters.DotTestListener test\testng.xml
以下が出力例になります。
........................................ ........................................ ........................................ ........................................ ........................................ ......................... =============================================== TestNG JDK 1.5 Total tests run: 226, Failures: 0, Skips: 0 ===============================================
-listener
を使う場合、TestNGはリスナの型を自動的に決定する点に注意してください。
6.2.2 - Logging Reporters
org.testng.IReporterにはメソッドが一つだけあります。
public void generateReport(List<ISuite> suites, String outputDirectory)
すべてのスイート実行後にTestNGがこのメソッドを呼び出します。テスト完了後のすべての情報にアクセスして検証を行えます。
6.2.3 - JUnitReports
TestNGの結果を受け取ってJUnitReportで処理可能なXMLファイルを出力するリスナがあります。こちらがサンプルです。
<target name="reports"> <junitreport todir="test-report"> <fileset dir="test-output"> <include name="*/*.xml"/> </fileset> <report format="noframes" todir="test-report"/> </junitreport> </target>
注意:JDK1.5とJUnitReportsには互換性が無いためフレームは動作しません。よって現在これを動作させるには"noframes"を指定する必要があります。
6.2.4 - Reporter API
HTMLレポートの生成時にログメッセージを出力したい場合、org.testng.Reporterクラスを使います。
Reporter.log("M3 WAS CALLED");
6.2.5 - XML Reports
JUnitレポートでは利用できないTestNG固有情報を参照するのにXMLレポーターをTestNGでは提供しています。ユーザのテスト環境がJUnitフォーマットでは提供していないTestNG固有のデータを処理する必要がある場合に有用です。以下はそうしたレポーター出力の例です。
<testng-results> <suite name="Suite1"> <groups> <group name="group1"> <method signature="com.test.TestOne.test2()" name="test2" class="com.test.TestOne"/> <method signature="com.test.TestOne.test1()" name="test1" class="com.test.TestOne"/> </group> <group name="group2"> <method signature="com.test.TestOne.test2()" name="test2" class="com.test.TestOne"/> </group> </groups> <test name="test1"> <class name="com.test.TestOne"> <test-method status="FAIL" signature="test1()" name="test1" duration-ms="0" started-at="2007-05-28T12:14:37Z" description="someDescription2" finished-at="2007-05-28T12:14:37Z"> <exception class="java.lang.AssertionError"> <short-stacktrace> <![CDATA[ java.lang.AssertionError ... Removed 22 stack frames ]]> </short-stacktrace> </exception> </test-method> <test-method status="PASS" signature="test2()" name="test2" duration-ms="0" started-at="2007-05-28T12:14:37Z" description="someDescription1" finished-at="2007-05-28T12:14:37Z"> </test-method> <test-method status="PASS" signature="setUp()" name="setUp" is-config="true" duration-ms="15" started-at="2007-05-28T12:14:37Z" finished-at="2007-05-28T12:14:37Z"> </test-method> </class> </test> </suite> </testng-results>
このレポーターはその他のデフォルトリスナで挿入されるため、デフォルトでこの種の出力を得られます。リスナは要求に合うようにレポーターを調整するためのプロパティがあります。以下の表はプロパティとその要約です。
プロパティ | コメント | デフォルト値 |
---|---|---|
outputDirectory | XMLファイルを出力するディレクトリを示すString |
TestNGの出力ディレクトリ |
timestampFormat | レポータが生成する日付フィールドのフォーマットを指定 | yyyy-MM-dd'T'HH:mm:ss'Z' |
fileFragmentationLevel | 1,2,3のいずれかのinteger で、それぞれXMLの生成方法を指定する。1 - 単一ファイルにすべての結果を生成する。 2 - スイートごとにXMLファイルが生成されてメインファイルへのリンクを持つ。 3 - 2に加えてテストケースごとに分割し、各ファイルはスイートファイルへの参照を持つ。 |
1 |
splitClassAndPackageNames | <class> 要素からクラス名を生成する方法をbooleanで指定する。たとえば、falseでは<class class="com.test.MyTest"> となり、trueでは<class class="MyTest" package="com.test"> となる。 |
false |
generateGroupsAttribute | <test-method> のgroups 属性を生成するかどうかをbooleanで指定する。This feature aims at providing a straight-forward method of retrieving the groups that include a test method without having to surf through the |
false |
generateTestResultAttributes | 個々の<test-method> 要素で<attributes> タグを生成するかどうかをbooleanで指定する。<test-method> 要素にはテスト結果の属性が含まれる(テスト結果属性の設定についてはITestResult.setAttribute() を参照)。各属性のtoString() したものが<attribute name="[attribute name]"> タグに出力される。 |
false |
stackTraceOutputMethod | 例外時に生成されるスタックトレースの種類を指定する。 0 - スタックトレース無し(例外クラスとメッセージのみ) 1 - スタックトレースの先頭数行のみの短い形式 2 - すべての内部例外を持つ完全なスタックトレース 3 - 短い形式と長い形式両方のスタックトレース |
2 |
generateDependsOnMethods | <test-method> のdepends-on-methods 属性の生成の有効化/無効化。 |
true |
generateDependsOnGroups | <test-method> のdepends-on-groups 属性の生成の有効化/無効化。 |
true |
レポーターの設定を行うにはコマンドラインで-reporter
オプションを使うかAntタスクでネストした<reporter>
要素を使います。どちらの方法においてもorg.testng.reporters.XMLReporter
クラスの指定が必須です。なお、組み込みのレポータの設定は出来ません。デフォルト設定を使うようにしか作られていないためです。カスタム設定でXMLレポート出力をしたい場合、二つのやり方のうち一つを手動で追加して*8デフォルトリスナーを無効化します。
7 - YAML
TestNGはスイートファイルの定義方法に別の方法としてYAMLをサポートしています。たとえば、以下のようなXMLファイルがあるとして、
<suite name="SingleSuite" verbose="2" thread-count="4"> <parameter name="n" value="42" /> <test name="Regression2"> <groups> <run> <exclude name="broken" /> </run> </groups> <classes> <class name="test.listeners.ResultEndMillisTest" /> </classes> </test> </suite>
YAMLではこうなります。
name: SingleSuite threadCount: 4 parameters: { n: 42 } tests: - name: Regression2 parameters: { count: 10 } excludedGroups: [ broken ] classes: - test.listeners.ResultEndMillisTest
TestNG自体のスイートファイルはこちらで、それに対応するYAMLはこちらです。
YAMLのほうが読んだりメンテナンスするのは容易なフォーマットかもしれません。YAMLファイルはTestNG Eclipseプラグインで読み込み可能です。YAMLとTestnNGに関するより詳細な情報についてはこのブログポストを参照してください。
*1:these points being either of the items listed above.が原文。aboveだけどリストは下にあるんだけど、何か意味があるのだろうか……
*2:Whether methods on this class/method are enabled. が原文。@Testはクラス・メソッドのどちらかに付与するので、methods on this class/methodとメソッドが二回出てくる重複表現になってると思われる。
*3:testng.xmlの-parallel=methods|tests|classesとか。
*4:To initialize your tests while wanting this initialization methods to be test methods as well (methods tagged with @Before/After will not be part of the final report). が原文。初期化メソッドだけどもテストメソッドとして扱いたいから実行順序が必要となる、という意味合いだとは思うが訳にちょっと自身が無い。
*5:Note that testng-failed.xml will contain all the necessary dependent methods so that you are guaranteed to run the methods that failed without any SKIP failures. が原文
*6:can vary from one run to the next が原文。
*7:よくわからん
*8:you will have to add it manually with one of the two methodsが原文。ここのmethodsはコマンドラインかantかの二択の「方法」を指すと思うんだけど…自信が無い。