Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

TestNG5.10ではデータプロバイダを使用した並列処理でのテストが追加される

Otaku, Cedric's weblog: Advanced parallel testing with TestNG and data providers

TestNGのデータプロバイダを利用してのより進んだ並列テスト


TestNGは別々のスレッドでテストを実行できる。スレッドプールサイズとタイムアウトを設定するだけでいい。TestNGがあとのことはやってくれる。たとえば、次のようなクラスを考えてみよう。スレッドプールのサイズは2と設定して呼び出してみる。

@Test
public class A {
  public void g1() { log("g1"); }
  public void g2() { log("g2"); }
  public void g3() { log("g3"); }
  public void g4() { log("g4"); }
}

結果はこうなる。

Thread:9 g4()
Thread:8 g2()
Thread:8 g3()
Thread:9 g1()

この結果からわかるように、TestNGは2つのスレッドのプールを生成し、すべてのテストメソッドを利用できるスレッドに振り分ける。スレッド戦略を設定することもできる(「テストメソッドごとに別のスレッドにする」「テストクラスごとに別のスレッドにする」など)し、各スレッドごとにタイムアウトを設定することもできる。


TestNGの有名な機能としてデータプロバイダもある。メソッドとデータプロバイダを2つずつ前のテストクラスに追加する。

@DataProvider()
public Object[][] dp1() {
  return new Object[][] {
      new Object[] { 1 },
      new Object[] { 2 },
      new Object[] { 3 },
      new Object[] { 4 },
  };
}

@Test(dataProvider = "dp1")
public void f1(Integer n) {
  log("f1", n);
}

@DataProvider
public Object[][] dp2() {
  return new Object[][] {
      new Object[] { 11 },
      new Object[] { 12 },
      new Object[] { 13 },
      new Object[] { 14 },
  };
}

@Test(dataProvider = "dp2")
public void f2(Integer n) {
  log("f2", n);
}

f1()は1、2、3、4と、f2()は11、12、13、14とパラメータが渡されて呼び出される。


実行するとこうなる(テストメソッドごとに色を変えた)。

Thread:9 g4()
Thread:8 g3()
Thread:9 g2()
Thread:8 f1(1)
Thread:9 f2(11)
Thread:9 f2(12)
Thread:9 f2(13)
Thread:8 f1(2)
Thread:9 f2(14)
Thread:8 f1(3)
Thread:9 g1()
Thread:8 f1(4)

まだスレッドプールのサイズは2で実行しているが、データプロバイダを使っている2つのメソッド(f1()とf2())は同一のスレッドで順番に呼び出されている。言い換えると、f1()は1つのスレッドで呼び出され、データプロバイダから値(1、2、3、4)をすべて受け取るまで同一スレッドに残ったままである。同様にf2()も値(11、12、13、14)を受け取るまで残っている。


データプロバイダへのマルチスレッド対応はTestNGでもっとも望まれていた機能だった。この実装が終わりTestNGの次のリリースに含められることをうれしく思っている。


スレッドプールでデータプロバイダを実行させるには、アノテーションのparallel属性を使う。

@DataProvider(parallel = true)
public Object[][] dp2() {

データプロバイダは別のスレッドで実行されるため、テストメソッドのスレッドとは異なるスレッドとなる。テストのスレッドプールを2、データプロバイダのスレッドプールを3に設定して先ほどのテストを実行するとこうなる。

Thread:9 g4()
Thread:8 g3()
Thread:8 g2()
Thread:9 f1(1)
Thread:10 f2(11)
Thread:11 f2(12)
Thread:12 f2(13)
Thread:9 f1(2)
Thread:12 f2(14)
Thread:9 f1(3)
Thread:9 f1(4)
Thread:8 g1()

この実行だとg*メソッドとf1()はテストのスレッドプールで実行している(f1()はデータプロバイダを使っていてparallel=trueを指定していないので、テストのスレッドプールを使っていることを思い出してほしい)。ここで目新しいことはf2()の4角メソッド呼び出しが3つの異なるスレッド(スレッド10、11、12)で起こっていることだ。これら3つのスレッドはデータプロバイダのスレッドプールにある。そのサイズは3に設定している。


同様に残りのデータプロバイダもparallelにしよう。

@DataProvider(parallel = true)
public Object[][] dp1() {

こうなる。

Thread:9 g4()
Thread:8 g3()
Thread:8 g2()
Thread:10 f2(11)
Thread:11 f1(1)
Thread:12 f1(2)
Thread:10 f1(3)
Thread:11 f1(4)
Thread:12 f2(12)
Thread:11 f2(13)
Thread:10 f2(14)
Thread:9 g1()

今度はg()メソッドだけがテストのスレッドプール(スレッド8、9)を利用している。データプロバイダを使った2つのメソッド(f1()とf2())はデータプロバイダのスレッドプール(スレッド10、11、12)を共有している。


この新機能で、TestNGを使った並行テストがより簡単になった。大量データを戻すデータプロバイダを使ったテストで実行時間を大幅に短縮できるだろう。


データプロバイダでの並行テストはTestNG 5.10に含まれるがベータバージョンはもうダウンロードできるので、ぜひ試してほしい。