読者です 読者をやめる 読者になる 読者になる

kagamihogeの日記

kagamihogeの日記です。

TestNGのDocumentationを読んで訳した

Java テストコード テキトー翻訳

TestNG使う機会があったのだけど、今まで使ったことがなかった。ので、とりあえずドキュメントの http://testng.org/doc/documentation-main.html 読んでテキトーに訳した。誤字脱字とか表記揺れ(ファクトリだったりファクトリ"ー"だったりする)があるけどあまりチェックしてないです。

1 - Introduction

TestNGはテストに関する様々な要求をシンプルにするよう設計されたテストフレームワークです。ユニットテスト(他クラスと独立した単一クラスのテスト)から統合テスト(複数クラス、複数パッケージ、アプリケーションサーバなど複数の外部フレームワークからなるシステム全体のテスト)まで対象とします。

テストの記述は一般的に3ステップのプロセスになります。

クイックスタートはWelcome pageにあります。

このドキュメントで使われる概念は以下となります。

  • スイート(suite)は一つのXMLファイルで表現する。スイートは一つ以上のテストを持ち、<suite>タグで定義する。
  • テスト(test)は<test>で表現し、一つ以上のTestNGクラスを持つ。
  • TestNGのクラスはJavaのクラスで少なくとも一つ以上のTestNGアノテーションを持つ。<class>タグで表現し、一つ以上のテストメソッドを持つ。
  • テストメソッド(test method)はソース内で@Testアノテーションを付与したJavaのメソッド。

TestNGのテストは@BeforeXXXおよび@AfterXXXアノテーションによる設定が可能で、なんらかのポイントの前後でJavaのロジックを実行可能です。それらのポイントは以下にリストを示します*1

以降のマニュアルでは以下を解説します。

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>

この例では、TestNGtest.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テストの実行など、があります。

デフォルトでは、TestNGXMLファイル上の順序でテストを実行します。もしこのファイル内のクラスとメソッドを未指定の順序で実行したい場合、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

また、TestNGJava 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.xmlinclusions/exclusionsでオーバーライドされます。

5 - Test methods, Test classes and Test groups

5.1 - Test methods

テストメソッドには@Testアノテーションを付与します。@Testを付与したメソッドの戻り値は、testng.xmlallow-return-valuestrueを指定しない限り、無視されます。

<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;
}

この場合、二つのメソッド引数dsdriverはプロパティdatasourcejdbc-driverでそれぞれ与えます。

引数はOptionalアノテーション付きで宣言することも可能です。

@Parameters("db")
@Test
public void testNonExistentParameter(@Optional("mysql") String db) { ... }

"db"という名前のパラメータがtestng.xmlで見つからない場合、テストメソッドは@Optionalアノテーションで定義したデフォルト値"mysql"を受け取ります。

@Parametersは以下の場合に使用可能です。

  • @Test, @Before/After, @Factoryアノテーションを持つ任意のメソッド。
  • テストクラスの最大1つまでのコンストラクタ。この場合、TestNGtestng.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レポートで参照できます。以下がその例です。

sample

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() {}

この例では、method1serverStartedOk()に依存すると宣言されています。これにより、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では赤でも緑でもない色)。スキップされたメソッドは必ずしも失敗したわけではない点に注意が必要です。

dependsOnGroupsdependsOnMethodsの両方ともパラメータとして正規表現が可能です。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属性により実行時にアロケートするスレッド数を指定可能です。

注意:@TesttimeOut属性はparallelnon-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

TestNGJUnit 3およびJUnit 4のテストの実行が可能です。クラスパスにJUnitのjarファイルを配置し、testng.classNamesプロパティにJUnitのテストクラスを指定してtestng.junittrueにします。

<test name="Test1" junit="true">
  <classes>
    <!-- ... -->

この場合のTestNGの振る舞いはクラスパス上のJUnitのバージョンに依存します。

  • JUnit 3:
    • クラス内のtest*ではじまるすべてのメソッドが実行される。
    • テストクラスにsetUp()メソッドがある場合、すべてのテストメソッドの前に実行される。
    • テストクラスにtearDown()メソッドがある場合、すべてのテストメソッドの後に実行される。
    • テストクラスがsuite()メソッドを持つ場合、そのメソッドが返すすべてのテストが実行される。
  • JUnit 4;
    • TestNGはテストの実行にorg.junit.runner.JUnitCoreを使用する。

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パッケージからXmlClassXmlTestなど、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) ;

上記のXmlSuiteTestNGに渡します。

List<XmlSuite> suites = new ArrayList<XmlSuite>();
suites.add(suite);
TestNG tng = new TestNG();
tng.setXmlSuites(suites);
tng.run(); 

API全体についてはJavaDocsを参照してください。

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 documentationlisteners属性を参照してください。

例えば、以下の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")と呼びます。

これらのインタフェースを実装する場合、以下の方法のいずれかを用いてTestNGに設定します。

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を拡張するクラスを指定可能です。ただし、IAnnotationTransformerIAnnotationTransformer2は除外します。TestNGアノテーションの書き換えに使用可能なため、処理のなるべく早期にリスナの存在をTestNGに知らせる必要があります。そのため、testng.xmlファイルでリスナを定義する必要があります。

注意点として、@Listenersアノテーションは、testng.xmlファイルに定義したかのように、スイートファイル全体に適用されます。スコープを制限したい場合(例えば、現在のクラスでの実行に限定する)リスナ内のコードでテストメソッドを実行するかどうか判定するチェックを行うように出来ます。

  1. まず制限を定義するのに使うための新規のカスタムアノテーションを定義します。
@Retention(RetentionPolicy.RUNTIME)
@Target ({ElementType.TYPE})
public @interface DisableListener {}
  1. リスナ内に以下のようなチェックを設けます。
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
}
  1. 呼び出したくないリスナのあるテストクラスに上記のアノテーションを付与します。

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を使う場合、TestNGGuiceモジュールを使用してテストオブジェクトをインジェクトする方法を用意しています。

@Guice(modules = GuiceExampleModule.class)
public class GuiceTest extends SimpleBaseTest {
 
  @Inject
  ISingleton m_singleton;
 
  @Test
  public void singletonShouldWork() {
    m_singleton.doSomething();
  }
 
}

このサンプルでは、GuiceExampleModuleISingletonインタフェースに何らかの具象型にバインドされるよう指定しています。

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-moduleguice-stageスイートパラメータを使うことで、Guiceの機能とさらなる柔軟性が得られます。guice-stageは親インジェクタ(parent injector)の生成で使用するStageを選択出来るようにします。デフォルトはDEVELOPMENTです。他に選択可能な値としてはPRODUCTIONTOOLがあります。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タグの中身を複数回複製して新しくスイートXMLファイルを作ることになります。動くことは動きますが、スケールはしません。

TestNGではリスナ警手で実行時にスイートXMLファイル内のテストやスイートタグを変更可能です。これにはIAlterSuiteListenerを実装するリスナを作成します。リスナについてはリスナのセクションを参照してください。

以下に実行時にスイート名を変更する方法を示します。

public class AlterSuiteNameListener implements IAlterSuiteListener {
 
    @Override
    public void alter(List<XmlSuite> suites) {
        XmlSuite suite = suites.get(0);
        suite.setName(getClass().getSimpleName());
    }
}

以下の方法のどちらかでリスナを追加可能です。

このリスナについては@Listenersアノテーションを使用した追加は出来ません。

6 - Test results

6.1 - Success, failure and assert

テストは、例外をスローせずに完了するか、期待される例外がスローされるか(@TestexpectedExceptions属性のドキュメントを参照)、であれば正常終了と見なします。

テストメソッドは基本的には、例外スロー可能な呼び出しにするか、各種のアサートで構成されます(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");

show-output1.png

show-output2.png

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 elements. 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かの二択の「方法」を指すと思うんだけど…自信が無い。