連載
» 2008年10月09日 00時00分 公開

実現場でTestNGを活躍させる“5”つのテクニック次世代テストフレームワークでテストを変える(3)(2/3 ページ)

[阪田浩一,株式会社クロノス]

サンプルを実行してみよう

 次に、ConcurrentTestクラスとAnotherConcurrentTestクラスのソースコードを示します。

package net.kronosjp.enkai.testng;
import org.testng.annotations.Test;
public class ConcurrentTest {
    @Test
    public void sampleA() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("★sampleAのスレッドID★" +             Thread.currentThread().getId());
    }
    @Test
    public void sampleB() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("★sampleBのスレッドID★" +             Thread.currentThread().getId());
    }
}
リスト3 ConcurrentTestクラス
package net.kronosjp.enkai.testng;
import org.testng.annotations.Test;
public class AnotherConcurrentTest {
    @Test
    public void sampleC() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("★sampleCのスレッドID★" +             Thread.currentThread().getId());
    }
    @Test
    public void sampleD() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("★sampleDのスレッドID★" +             Thread.currentThread().getId());
    }
}
リスト4 AnotherConcurrentTestクラス

 両方ともテストメソッドを2つ作成し、テストメソッドでは1000ミリ秒(1秒)待機してスレッドのIDを出力します。待機するのは実行時間の比較を分かりやすくするためです。では、このテストをtestng.xmlから実行します。

★sampleDのスレッドID★9
★sampleBのスレッドID★8
★sampleAのスレッドID★8
★sampleCのスレッドID★9
実行結果

 テストの実行順序はランダムですが、ConcurrentTestクラスのテストメソッドsampleAとsampleBを同じスレッドで、そしてAnotherConcurrentTestクラスのテストメソッドsampleCとsampleDを同じスレッドで実行しています。Eclipseで実行すると、次のように表示します。

図2 自動的に作成された「testng-failed.xml」 図2 自動的に作成された「testng-failed.xml」

 並列実行しているためかグリーンバーがうまく表示されませんが、すべてのテストは成功しています。

並列実行しないときと比較すると……

 では、本当に実行時間を短縮できているのか確認しましょう。testng.xmlでparallel属性とthread-count属性を削除します。そしてテストを実行します。すると、次のような結果になりました。

図3 並列実行しない場合のテスト結果 図3 並列実行しない場合のテスト結果

 2つの場合の実行時間を一覧に示します。

並列実行の有無 実行時間
あり 2357ms(約2.3秒)
なし 4329ms(約4.3秒)
表 実行時間の比較

 並列実行した場合の方が約2倍速くなるという結果になりました。

積極的にテストを並列実行しよう

 実際のプロジェクトにおいても、常に2倍速くなるとは限りませんが、実行時間を短縮できることは間違いありません。テストの実行時間が短ければ、頻繁にテストを実行でき、その分フィードバックを速く得られバグを修正するコストが低くなります。TestNGの並列実行機能は積極的に利用しましょう。

 ただし、テスト対象のメソッドがスレッドセーフでない場合、並列実行したときに本来成功するはずのテストが失敗してしまうことがあるかもしれません。そのようなときは<test>要素の内容をうまく組み替えてテストを実行してください。

【3】いままでのJUnitのテストはドースル?

 過去にJUnitのテストを作成していて、いままでのJUnitテストを無駄にしたくないという場合もあります。JUnitのテストが資産としてある場合でもTestNGを利用できるように、TestNGには便利な機能が2つあります。

【便利機能その1】TestNGとJUnitのテストをまとめて実行!

 TestNGの設定ファイルtestng.xmlを使ってテストを実行する際に、JUnitのテストも一緒に実行できます。JUnitのテストを実行するtestng.xmlを次に示します。

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="junitsample">
  <test name="junittests" junit="true">
    <classes>
      <class name="net.kronosjp.junit3.Junit3Sample"></class>
    </classes>
  </test>
</suite>
リスト5 JUnitのテストを実行するtestng.xml

 <test>要素のjunit属性の値を「true」にします。すると、TestNGはその<test>要素に含まれるクラスをすべてJUnitのテストとして実行します。これを「JUnitモード」といいます。

 JUnitモードにより、JUnitのテストの実行をTestNGと分けて管理する必要はなく、TestNGのテストと一緒にまとめて実行できます。

【便利機能その2】JUnitのテストをTestNGのテストに変換!

 さらにTestNGにはJUnit 3で作成したテストをTestNGのテストに変換するコンバート機能があります。Antで変換を実行できるため、JUnitのテストが大量にあっても一気に変換できます。Antのbuild.xmlを次に示します。

<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="default">
  <taskdef classpath="testng-X.X-jdk15.jarへのパス"
    resource="testngtasks" />
  <target name="default">
    <junit-converter
      sourcedir="JUnitのテストがあるディレクトリへのパス"
      outputdir="変換したソースファイルを出力するディレクトリへのパス"
    />
  </target>
</project>
リスト6 JUnitのテストを変換するbuild.xml

 コンバート機能を利用するために、<taskdef>要素を使ってTestNGのタスク定義を追加します。これで、<junit-converter>要素を使用できます。

 sourcedir属性には、JUnit 3のテストがあるディレクトリへのパスを指定します。また、変換したテストクラスをoutputdir属性に指定したディレクトリに出力します。JUnitのテストを上書きせず、新しいファイルを作成するため気軽に使用できます。

変換前後を見比べてみよう

 では、実際にコンバート機能を使います。まず変換前のJUnit 3で作成したテストクラスを次に示します。

import junit.framework.TestCase;
public class Junit3Sample extends TestCase {
    protected void setUp() throws Exception {}
    protected void tearDown() throws Exception {}
    public void testAdd() {}
    public void sample() {}
}
リスト7 変換前のJUnit 3テストクラス

 このクラスを前述のようなbuild.xmlを使って変換します。変換後のTestNGテストクラスを次に示します。

import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import junit.framework.TestCase;
public class Junit3Sample extends TestCase {
    @BeforeMethod
    protected void setUp() throws Exception {}
    @AfterMethod
    protected void tearDown() throws Exception {}
    @Test
    public void testAdd() {}
    public void sample() {}
}
リスト8 変換後のTestNGテストクラス

 TestNGのアノテーションが適切に付与されたことが分かります。setUp()メソッドには@BeforeMethodが、tearDown()メソッドには@AfterMethodが、testで始まる名前のメソッドには@Testアノテーションが付きます。テストメソッドでない通常のメソッドには、何も影響しません。

JUnitモードで実行しながら徐々に変換!

 上記の解説のとおりJUnitのテストがすでにある場合でも、JUnitモードでTestNGのテストと一緒に実行できます。そして、少しずつコンバート機能を使ってJUnitのテストをTestNGのテストに変換します。TestNGのテストであれば、豊富な機能を利用できるからです。

 TestNGの機能を利用して「よりよいテスト」に変えていくことでテストを活用できる期間を延ばすことができます。

 次ページでは引き続き、4つ目と5つ目のテクニックを紹介し、最後にTestNGやJUnitといったテスティングフレームワークを実現場でのプロジェクトで導入するためのヒントをお話します。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。