2015/03/21 このエントリーをはてなブックマークに追加 はてなブックマーク - 【JUnit】出来るの?かもしれない@Aftrer処理をメソッド単位でSkipする方法

【JUnit】出来るの?かもしれない@Aftrer処理をメソッド単位でSkipする方法






例えば、テストクラスで@Afterな処理があったとして、
どうしてもTestA、B、Cではその後処理が必要なのだけれども
TestEで不要だということありませんか???ありますよね?(多分あんまりない)。

TestEのためだけに@Afterで定義しているメソッドをいじるのはなんだか嫌ですよね



これはテストがNなのに対して@Afterの処理が1なので起こり得る問題なのではないかと思います。
そういう時のためのTipsです。






JUnitのTestRuleを継承してカスタムルールを作りうまいことやる。

具体的には、カスタムルールの中で、 @Afterな処理を行いたくないメソッドを判定してスキップさせます。





用意するものは以下の3つです。


・アノテーションクラス
・TestRuleカスタムルールクラス
・After処理インターフェースクラス








まず、アノテーションから。After処理を行いたくないメソッドに
つけるためのアノテーションクラスです。こんな感じです。



@Retention(RetentionPolicy.RUNTIME)
@Target({ java.lang.annotation.ElementType.METHOD })
public @interface DontRunAfter {

}





続いて、カスタムルールを作ります。DontRunAfterアノテーションが取得できなかったもののみ、
あと処理を行います。インターフェースを介してteardownというメソッドを実行する作りにしています。

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class IgnoreAfterRule implements TestRule {

    /** after処理リスナー */
    private AfterListener testCase;

    /**
     * コンストラクタ
     * @param testCase after処理リスナー
     */
    public IgnoreAfterRule(AfterListener testCase) {
        this.testCase = testCase;
    }

    /**
     * ルール適用処理を行います
     * 
     * @param base
     *            ステートメント
     * @param description
     *            テストディスクリプション
     */
    @Override
    public Statement apply(final Statement base, final Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                base.evaluate();
                if (description.getAnnotation(DontRunAfter.class) == null) {
                    testCase.tearDown();
                }
            }
        };
    }
}




最後にカスタムルールクラス内で呼び出していた@Beforeの代替となるインターフェースです。
実装はテストクラスに任せます。


public interface AfterListener {
    void tearDown() throws Exception;
}
テスト実行は以下のように行います。
@Rule
public IgnoreAfterRule ignoreRule = new IgnoreAfterRule(new AfterListener(){
    @Override
    public void tearDown() throws Exception {
    }});

@Test
@DontRunAfter
public void test() throws Exception {
}








メリット・デメリットはこんな感じです。

メリット
・After処理とテストメソッドを疎結合にできる


デメリット
・JUnitのAfterアノテーションによるライフサイクルが使えなくなる







今回はAfter処理をメソッド単位で実行するかしないか
コントロールする方法を書いてみました。



Afterの処理は本来、汎用的な処理にとどめるべきであり、
どこかのテストに依存するべきではありません。
したがって、N:1の関係でも問題は起こりにくいと思います。

そういったテストフレームワークの思想を少し変えてしまうのかなぁという
悪手の紹介でした笑


こういった手法が必要になるのは結構レアです。
必要になってしまう場合は、
もしかするとテストクラスやテスト対象のクラスの設計が良くないのかもしれません。

しかし、どうしても必要になる場合もあるかもしれないかもしれないので、
とりあえずTipとして残すこととします。




今回は例えば、20クラス × 各クラス10メソッド = 200のメソッドがあったとして、
195のメソッドで同一のAfter処理で、5つのメソッドのみAfter処理が必要なかった、とかそういう局所的なケースを想定しています。


テストクラス単位で@Beforeと@Aftrerを完全に分割できるのであれば、 JUnitのEnclosedをテストランナーとすると良いようです。

JUnit4のRunner概説 - penultimate diary
Enclosedと継承を使って特定のメソッドだけAfterとか実行させない - mike-neckのブログ

0 件のコメント:

コメントを投稿