Ruleを使ったJUnit4.7以降の拡張方法
前回はテストメソッド実行用Statementクラスを直接拡張しましたが、今回は@Ruleアノテーションを使った拡張方法のエントリーを書いてみます。
JUnit4.7以降のバージョンには@RuleというアノテーションとMethodRuleというインターフェースが用意されています。
(11/2 追記:4.9以降はMethodRuleが非推奨になっています。MethodRuleの替わりにTestRuleというのが出来ているので4.9以降を使用する場合はTestRuleを使うようにしましょう。インターフェースのシグネチャが多少違いますが、やってることは同じです。)
JUnit4.7以降はMethodRuleの代表的な実装としてExpectedExceptionがあります。こやつはテストケース内での例外をテストしてくれるやつですが4.6までだと
@Test(expected=IllegalArgumentException.class) public void exceptionTest(){ new Hoge().invoke(); }
上記のように例外の型だけしか検査できず、例外のメッセージをアサートする場合は自力でいろいろやらないといけませんでした。しかしExpectedExceptionを使用すると下記のようになります。(@Ruleを付加するフィールドはpublicでなければいけません。)
@Rule public ExpectedException exception = ExpectedException.none; @Test public void exceptionTest(){ exception.expect(IllegalArgumentException.class); //このコードでExceptionの型をテスト exception.expectMessage("えらー"); //このコードでエラーメッセージをテスト可能 new Hoge().invoke(); }
んで、上記をふまえた上で前回のような、あるテストクラス内で特定のメソッド(テストケース)のみでいろいろやりたいといった場合に@RuleとMethodRuleを実装したクラスを使ったやり方は下記のようになります。
まずはRuleクラスの作成
public class HogeRule implements MethodRule{ @Override public Statement apply(final Statement base,final FrameworkMethod method,final Object target) { return new Statement() { @Override public void evaluate() throws Throwable { if(method.getAnnotation(MyAnnotation.class) != null) System.out.println("Annotation 発見。"); System.out.println("かいし〜"); base.evaluate(); System.out.println("しゅーりょ〜"); } }; } }
(11/2 追記 4.9以降でMethodRuleではなくTestRuleを使う場合は下記のようになります。)
public class HogeRule implements TestRule{ @Override public Statement apply(final Statement base,final Description description) { return new Statement() { @Override public void evaluate() throws Throwable { if (description.getAnnotation(MyAnnotation.class) != null) { System.out.println("Annotation 発見。"); } System.out.println("かいし〜"); base.evaluate(); System.out.println("しゅーりょ〜"); } }; } }
んで、このHogeRuleを使うテストクラスは下記のようになります。
public class RuleTest { @Rule public HogeRule rule = new HogeRule(); @BeforeClass public static void beforeClass(){ System.out.println("before class!"); } @AfterClass public static void afterClass(){ System.out.println("after class!"); } @Before public void before(){ System.out.println("before!"); } @After public void after(){ System.out.println("after!"); } @MyAnnotation @Test public void test1() { System.out.println("test1"); } }
これを実行した場合、コンソールには下記の順番で出力されます。
before class! Annotation 発見。 かいし〜 before! test1 after! しゅーりょ〜 after class!
注意しないといけないのはHogeRule内の無名Statementクラス内の出力処理(Annotation 発見。)は@Beforeよりも先に呼ばれる点でしょうか。
こんな感じで@RuleアノテーションとMethodRuleを使えばいろいろ拡張することが可能になります。
しかし、個人的にpublicフィールドというのがどうしても嫌なんですよねぇ〜。privateフィールドもOKにするにはかなり拡張しないといけなくなります。そのくらい別にいいじゃんという意見はよくわかるのですが・・・・。ただし、4.7以降を使う場合はやはり前回のやり方よりも今回の@Ruleを使う方がJUnitの作法としては良いはず・・・。う〜ん悩ましいです。
なお、MethodRuleの拡張方法はorg.junit.rules.ExpectedExceptionクラスかorg.junit.rules.TestWatchmanクラスをみると非常にわかりやすいと思います。