Skip to content.

Sections
Personal tools
You are here: Home » コミュニティ » masarl memorial » homepage3.nifty.com » masarl » article » junit » JUnit 実践講座 - Tips集

JUnit 実践講座 - Tips集

Document Actions

JUnit 実践講座 - Tips集

2003/12/23 更新 石井 勝

What's New

目次

CommonTestCaseにヘルパーメソッドを集める

テストケースは通常junit.framework.TestCaseを継承して作成しますが,各プロジェクトに特化した共通のテストケースを作り,そのクラスを継承してテストケースを作成する方が便利なことがあります.例えばそのクラスをCommonTestCaseという名前にしておき,次のようにします.

public class CommonTestCase extends junit.framework.TestCase
{
   // プロジェクトに特化したヘルパーメソッドを定義
}

そして,そのプロジェクトのテストケースはすべてCommonTestCaseから継承するようにします.

public class FooTest extends CommonTestCase
{
   // CommonTestCaseのヘルパーメソッドを利用
}

例えば,僕が以前行なった開発ではテストメソッドの中で日付を頻繁に扱う必要がありました.そこで,Calendarオブジェクトが簡単に作れるようtoCalメソッドというヘルパーメソッドを用意していました.

// 2003年1月1日が休日であることのテスト
assertEquals(true, businessCalendar.isHoliday(toCal("2003/01/01")));
// 次の営業日が1月5日であることのテスト
assertEquals(toCal("2003/01/05"), 
                   businessCalendar.getNextWorkingDate(toCal("2003/01/01")));

toCalメソッドは,日付の文字列を引数にとってその日のCalendarオブジェクトを返します.他にも,その日付が今日かどうかを判定するassertTodayメソッドや,正規表現を使ってアサートを行なうassertMatchesメソッドなども用意していました.

TestSaverで後片付けをする

Singletonなどstaticなデータを含むテストコードを書くのは面倒な作業です.各テストメソッド終了後,それらの値を確実にもとに戻さなくてはなりません.そうしないと次のテストメソッドに影響がおき,予期せぬエラーが発生してしまうことがあります.あるいは,テストメソッドでリソースを取得し,メソッド終了後そのリソースを破棄しなければならない場合を考えてみましょう.テストメソッドをtry ... finally で囲めば対応できますが,そのテストメソッドが別のメソッドを呼んでおり,その内部でリソースを取得していた場合はtry ... finallyを使っても単純にうまくはいきません.

JUnitでは準備と後片付けのためにsetUp, tearDownメソッドが用意されていますが,すべてのテストメソッドで共通に呼ばれるためテストメソッドごとに終了処理を制御することは困難です.こういった場合,TestSaverインターフェイスを作ると便利です.

public interface TestSaver
{
    public void tearDown() throws Exception;
}

そして,CommonTestCaseにTestSaverに関する機能を用意しておきます.

import java.util.*;
import junit.framework.TestCase;

public class CommonTestCase extends TestCase
{
    private List testSavers = new ArrayList();

    protected void tearDown() throws Exception
    {
        for (int i = testSavers.size() - 1; 0 <= i; --i) {
            TestSaver saver = (TestSaver) testSavers.get(i);
            saver.tearDown();
        }
        testSavers.clear();
        super.tearDown();
    }

    protected void addTestSaver(TestSaver saver)
    {
        testSavers.add(saver);        
    }
    ....
}

仕組みは簡単です.addTestSaverメソッドで登録したTestSaverをtearDownメソッドで逆順に呼んでいるだけです.

例えば,アプリケーションのユーザ設定を表すクラス: PreferencesがSingletonパターンで実装されており,テストコード内で一時的に値を変えなければならない場合を考えてみましょう.このとき,次のようなTestSaverクラスを作成します.

class PreferencesSaver implements TestSaver
{
    private String name;
    private String value;
    public PreferencesSaver(String name)
    {
        this.name = name;
        value = Preferences.getInstance().getValue(name);
    }
    public void tearDown() throws Exception
    {
        Preferences.getInstance().setValue(name, value);
    }
}

PreferencesSaverは,ある名前のユーザ設定を保存しておき,tearDownメソッドで元に戻すクラスです.このときテストコードは以下のようになります.

/**
 * フォントサイズが変わった場合のテスト
 */
public void testFontSize() throws Exception
{
    addTestSaver(new PreferencesSaver("fontSize"));
    Preferences.getInstance().setValue("fontSize", "12pt");

    // フォントサイズが12ptの場合のテストを実行
    ....
}

最初にPreferencesSaverをTestSaverとして登録しているため,テスト内では自由にfontSizeを変えることができて便利です.終了処理がちゃんと行われているかどうか気にする必要がありません.

assertEqualsのエラーメッセージを省略されないようにする

JUnit3.8.1からは,期待値と実測値に文字列を使うと,assertEqualsのエラーメッセージ(expected:<...> but was<...>)で同じ部分が省略表示されるようになりました.

There was 1 failure:
1) test(FooTest)junit.framework.ComparisonFailure: expected:<...foo...> but was:<...bar...>

これでは全体のどの部分が間違っているかよくわかりません.これを防ぐには,assertEquals呼び出しでObjectのキャストを入れます.

assertEquals((Object)expected, actual);

こうすれば完全な形でエラーメッセージを出力してくれます.

また,プロジェクトにCommonTestCaseを定義している場合は,次のようにすればOKです.

import junit.framework.TestCase;
public class CommonTestCase extends TestCase
{
    public static void assertEquals(String expected, String actual)
    {
        assertEquals((Object)expected, actual);
    }
    ....
}

こうすれば,各assertEqualsメソッドで面倒なObjectのキャストを書かずにすみます.

期待値と実測値のdiffをとる

JUnitの開発では,assertEquals が出力するエラーメッセージについて

  1. Failure, Error の発生箇所にすぐジャンプできる
  2. 期待値と実測値の diff を簡単にとることができる

ことが必要です.2番目の機能についてEclipseおよびEmacs用のツールを用意しました.ご自由にお使いください.

Eclipseプラグイン - JUnit Diff

JUnit Diffは,Eclipseで期待値と実測値のdiffをとるためのプラグインです.Eclipse 2.1.1 での動作を確認しています.

JUnit Diff

ダウンロード

ダウンロードしたzipファイルを解凍し,Eclipseのpluginsフォルダに展開してください.

バージョン 公開日 ダウンロード 説明
1.1.1 2003/12/23 junit.extensions.eclipse.diff_1.1.1.zip Readme.txt
1.0.1 2003/08/24 junit-diff_1.0.1.zip Readme.txt
1.0.0 2003/08/17 junit-diff_1.0.0.zip -

使い方

「ウィンドウ(w)」―「ビューの表示(V)」―「その他(O)...」メニューを選択して「ビューの表示」ダイアログを表示させます(ver 1.1.1からこの操作は必要ありません.JUnitと連動して必要なときに自動的に表示されるようになりました).

ビューの表示ダイアログ

「ビューの表示」ダイアログのJavaフォルダにあるJUnit Diffを選択してOKボタンを押してください.これでJUnit Diffビューが表示されます.JUnitビューで失敗したテストを選択すると,連動してそのテストのdiffを表示します.

注意

このプラグインは,org.eclipse.jdtプラグイン内にあるJUnitビューの内部実装に深く依存しています(ソースを見ればわかりますが,リフレクションを駆使してパッケージプライベートなクラスやプライベート変数に直接アクセスするという極悪なことをしています(T_T).).少しでもバージョンが変わると動作しない可能性がありますのでご注意ください.

Emacsコマンド - ediff-junit-expected-but-was

Emacsのediff機能を使って期待値と実測値のdiffをとることができます(ダウンロード).

ediff-junit-expected-but-wasコマンド

このコマンドはcompilation バッファで用います.RubyUnit の場合は \C-c\C-r で, JUnit の場合は \C-c\C-j というキーバインドでdiffをとることができます.

更新履歴