読者です 読者をやめる 読者になる 読者になる

日々常々

ふつうのプログラマがあたりまえにしたいこと。

PowerMockとJUnitのRuleを使うときのメモ

JUnit

少し試行錯誤してしまったので書いときます。この情報はバージョンによって古くなる可能性が十分にあります。

  • PowerMock: 1.5
  • Mockito: 1.9.5
  • junit: 4.11

short answer

PowerMockRule は使わず PowerMockRunner を使いましょう。

PowerMock?

いわゆるMockのアレですが、名前の示す通りPowerある感じです。staticとかをアレできるところとかが。使い方はJUnitのRunnerで指定するのが基本です。

@RunWith(PowerMockRunner.class)
@PrepareForTest(Fuga.class)
public class HogeTest {
    //...
}

これの良いところは「うわーまたMockの使い方覚えなきゃなのかよ……」なんて私のために、MockitoやEasyMockの形で書けるところです。PowerMockito とか見ればわかるように Mockito クラスの代わりに PowerMockito を使えばだいたいそんなノリで使えます。どっちをstatic-importしたか忘れる事もしばしばありますが、それで困った記憶はあまり無いです。

Runner を使いたくない

JUnit4 以降を使うことで継承の呪縛から解き放たれた我々ですが、依然として一つのものしか使えない特権階級が居ます。それが @RunWith で指定する Runner さん。Runner さんをいじると色んな事が出来て万能感あるのですが、使えるのは一つだけになってしまう辺り残念な感じになります。なので出来るなら Rule で閉じて Runner は最後の手段に置いておきたい。

PowerMockRule がある

Rule さんを用意してくれてます。

@PrepareForTest(Fuga.class)
public class HogeTest {
    @Rule
    public PowerMockRule rule = new PowerMockRule();

    //...
}

わるくない。*1公式のWikiでは PrepareForTest アノテーションの後ろにセミコロンが入ってたり、Rule が public でなかったりで、コピペじゃ動きません。ハードルたけぇ。

いい感じな気がしたのですが、これ……複数Rule指定すると動かなくなります。同じような人居ました。

JUnit4.7で追加された MethodRule さんですが、JUnit4.9で TestRule さんが追加され、これに置き換えられていく感じでございます。

* Note that {@link MethodRule} has been replaced by {@link TestRule},
* which has the added benefit of supporting class rules.

Rule にはJUnit4.9以降 @Rule と @ClassRule ができて MethodRule は合わなくなっちゃったんだけど、名前変更じゃなく追加なのは MethodRule を自分で実装しちゃってる人も結構いたからなんでしょうね。公開インタフェースは難しいです。

と言うことで結論

JUnitを使って Rule を使わないなんて選択は呼吸困難に陥るので、生命維持のために「PowerMockRule は使えない」になります。
PowerMock を使うときは PowerMockRule は使わず PowerMockRunner を使いましょう。

@RunWith(PowerMockRunner.class)
@PrepareForTest(Fuga.class)
public class HogeTest {

    @Rule
    public ExpectedException ex = ExpectedException.none();

    //...
}

どっちも Rule で出来ればよかったんだがな……(´・ω・`)


余談: RuleChain さん

Rule の順序をハッキリさせられる RuleChain さんがJUnit4.10 で追加されているのですが、これ TestRule しか見てくれません。MethodRule は使っちゃダメですね。その辺を気にせずまとめて Rule と認識して RuleChain さんを使おうとすると、コンパイル出来なくて首を傾げる事になります。ここに書いている事は忘れて私と一緒に首を傾げましょう。

*1:使わないRuleフィールドを宣言しなきゃいけないのは筋が悪い気もしてる今日この頃。