1. HOME »
  2. Java Tips »
  3. Technology & Programming

Technology & Programming Article

ソーシャルブックマークに登録 : Yahoo!ブックマークに登録 はてなブックマークに登録 del.icio.usに登録 newsing it!に登録 Buzzurlにブックマーク livedoorクリップに登録 Choix!にブックマーク イザ!ブックマーク

print 印刷用ページの表示

一段上を行くテスティング・テクニック20選

JUnitスーパーTips

第1回 基礎編

ユニット・テスト(単体テスト)用フレームワークのデファクト・スタンダードとも言われるJUnitだが、実際には、これをそのまま使うだけでは、実現できないテストも多い。他の優れたフレームワークと同様に、JUnitも、利用者の目的に合わせた拡張やカスタマイズが必要なのだ。本連載では5回にわたって、このJUnitをテスト・ツールとしてより効果的に使う、あるいはテストや設計を改善するためのツールとしても活用するという観点から、20個のTipsをお届けする。第1回となる今回は、多くのテストに共通して適用可能な基本的なTipsを紹介する。

2006年3月6日更新

text ●  安井 力 永和システムマネジメント/懸田 剛、梅田 政利 チェンジビジョン

Tips01
テストの前処理を1回だけにする──テストの高速化──

解決すべき問題

 JUnitでは、テスト・ケースのスーパークラスとなるクラスjunit.framework.TestCaseに、テストの前処理/後処理を行うためのメソッドsetUp/tearDownが用意されている。これらは、各テスト・メソッド(テスト・ケースに書かれたテスト用のメソッド)が呼び出される度に実行される。

 テストの前処理が時間のかかるものである場合、テスト・ケースの数が増えると、テスト全体を実行するのに多大な時間を費やすことになる。つまり、ちょっとした変更に対する安全性の確認のために、長い時間をかけてテストを実行しなければならず、開発の効率が悪くなってしまうのだ。なかには、それが原因でテストを行わなくなる人が出てくる。

解法と効果

 この問題に対する1つの解は、「前処理を1回だけ実行するようにする」というものになる。各テストによって副作用が発生することがない(テストの実行によって、何らかの状態の変更が生じることがない)、あるいは副作用が発生したとしても、それを初期化できるという前提条件を満たす場合に、前処理を1回だけで済ませるかたちでテストを行うことができる。これにより、GUIも含めてテストしているようなケースなどでは、実行時間が大幅に短縮されるだろう。

 以下、具体的な解決法を2つ紹介する。

1つのテスト・ケースに対する解法

 まず、1つのテスト・ケースにおいて、テスト・メソッドがいくつあるかにかかわらず、1回だけ前処理が行われるようにする方法をご覧いただこう。

 リスト1に示したテスト・ケースでは、メソッドsuiteで、通常のjunit.framework.TestSuiteオブジェクト(テスト・スイート)を返すのではなく、TestSuiteオブジェクトを保持するjunit.extensions.TestSetupオブジェクトを返すようにしている。これにより、オーバーライドしたメソッドTestSetup.setUp(ならびに、メソッドTestSetup.tearDown)が、メソッドTestSuite.runが呼び出される前後で実行される仕組みとなっている。

リスト1:1つのテスト・ケースの実行時に1回だけ前処理を行う例
package jp.jw.sample.tips1_1;
 
import junit.extensions.TestSetup;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
 
public class XxxxTest extends TestCase {
   public static Test suite() {
      TestSuite testSuite = new TestSuite(XxxxTest.class);
      TestSetup wrapper = new TestSetup(testSuite) {
         protected void setUp() {
            // テスト全体の前処理を記述
         }
         protected void tearDown() {
            // テスト全体の後処理を記述
         }
      };
      return wrapper;
   }
 
   protected void setUp() {
      // テストに副作用がある場合、初期化処理を記述
   }
 
   public void testXxxx1() {}
 
   public void testXxxx2() {}
}

 この方法の場合、通常のJUnitのテストと同様に実行できる。

複数のテスト・ケースに対する解法

 続いて、複数のテスト・ケースをまとめて一度に実行する際、1つ目のテスト・ケースの実行前に、1回だけ前処理を行う方法を見ていただこう。

 リスト2に示したのが、その実装例である。この方法では、JUnit自体に変更を加えることなく動作を拡張していることになり、TestSuiteオブジェクトを実行する前後で、好きなように前処理と後処理を記述できる。

リスト2:複数のテスト・ケースの実行時に、1回だけ前処理を行う例(その1)
package jp.jw.sample.tips1_2;
 
import junit.framework.Test;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
 
public class XxxxAllTestRunner {
   public static Test suite() {
      TestSuite testSuite = new TestSuite();
      // テスト・ケースを(複数)追加
      testSuite.addTestSuite(XxxxTest.class);
      return testSuite;
   }
 
   private static void setUp() {
      // テスト全体の前処理を記述
   }
 
   private static void tearDown() {
      // テスト全体の後処理を記述
   }
 
   public static void main(String[] args) throws Exception {
      setUp();
      TestRunner.run(suite());
      tearDown();
   }
}

 ただし、この方法の場合、複数のテスト・ケースを通常のJavaアプリケーションとして実行することになる。

さらなる応用例

 上記のどちらの方法においても、各テストに副作用がない場合、クラスTestCaseのメソッドsetUpをオーバーライドする必要はない。一方、副作用がある場合には、クラスTestCaseのメソッドsetUpで副作用を初期化する処理を行う。

 ここでは、一般的に紹介されている2つの方法を取り上げたが※1、後者の方法の場合、メソッドmainから実行することになるため、統合開発環境(IDE)として「Eclipse」を用いている場合、通常のJUnitの利用法とは異なることになり、多少使い勝手が悪い。このことが問題になるなら、前者の方法を併用し、リスト3のようにして複数のテスト・ケースを実行するとよいだろう。クラスTestCaseを拡張したクラスにテスト・メソッドが1つも記述されていないというのは多少気持ち悪いが、これであれば、通常のJUnitのテストと同様に実行できる。

※1 ここで示した方法は、『JUnit Recipes: Practical Methods for Programmer Testing』(著者:J.B.レインズバーガー、スコット・スターリング/発行:Manning Publications/発行年:2004年)を参考にしたものである。

リスト3:複数のテスト・ケースの実行時に、1回だけ前処理を行う例(その2)
import junit.extensions.TestSetup;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
 
public class AllTests extends TestCase {
   public static Test suite() {
      TestSuite testSuite = new TestSuite();
      // 複数のテスト・ケースを追加
      testSuite.addTestSuite(XxxxTest.class);
 
      TestSetup wrapper = new TestSetup(testSuite) {
         protected void setUp() {}
         protected void tearDown() {}
      };
      return wrapper;
   }
}

ページの先頭へ戻る