以前の日記でEasyMockをご紹介したのですが、現在の現場ではMockitoを使っています。
それにしてもモックキット?モキット?モキート?どう読むんだろう…
ロゴは有名なカクテル「モヒート」ではあるが。僕はミントが苦手なので余り飲まないけど。
Mockitoは比較的新しいモックユーティリティです。
何か革新的な機能を備えているかというとそうでもないのですが、使い勝手が非常に良いのが特徴。
サンプルコードはこんな感じ。
public class VeryHugeBusinessLogic { void doSomethingHardProcessing() { ... } } public class VeryHugeService { @Autowired VeryHugeBusinessLogic logic; public void execute() { logic.doSomethingHardProcessing(); } } /** * VeryHugeServiceのテスト */ public class VeryHugeServiceTest { public void testExecute() { VeryHugeBusinessLogic mockLogic = Mockito.mock(VeryHugeBusinessLogic.class); VeryHugeService service = new VeryHugeService(); service.logic = mockLogic; service.execute(); // doSomethingHardProcessingが呼ばれたことを確認 Mockito.verify(mockLogic).doSomethingHardProcessing(); } }
spy最高!
さて、ここまでの説明を読んで「EasyMockと大して変わんなんくね?」と思ったそこの貴方。
違うんです。Mockitoには1つだけ、EasyMockにはマネできない素晴らしい機能が存在するのです。
それは何かと言うと...Mockito#spyメソッド。
ほとんどこれだけのために、僕はMockitoへ乗り換える決心をしました。
例えば、次のようなクラスを考えてみます。
import java.sql.*; public class FooBusinessLogic { public void execute() { if (isAnyCondition()) { doSomething(); } else { doOtherthing(); } } protected boolean isAnyCondition() { // DBに接続しに行く複雑な処理 } protected void doSomething() { // やはりDBに接続しに行く複雑な処理 } protected void doOtherthing() { // こいつもDBに接続しに行く複雑な処理 } }
こんなクラスがあったとき、従来ならばJUnitで
import org.junit.*; public class FooBusinessLogicTest { /** * isAnyConditionがtrueを返し、doSomethingが呼ばれることを検証する。 */ @Test public void testExecuteDoSomethingInvoked() { // doSomethingが実行されたか否かの判定フラグ final boolean[] doSomethingInvoked = { false }; FooBusinessLogic logic = new FooBusinessLogic() { @Override protected boolean isAnyCondition() { return true; } @Override protected void doSomething() { doSomethingInvoked[0] = true; } @Override protected void doOtherthing() { Assert.fail(); } }; logic.execute(); // doSomethingが呼ばれたことを確認 Assert.assertTrue(doSomethingInvoked[0]); } }
とかやっていたわけです。
ホラでた〜!! Javaのダメダメランキング一位の無名クラス。
あと、doSomethingInvoked[0]のあたりも美しくない。
こんな見苦しいコードが、Mockkitoを使うとこんなキレイに変身するわけです。
import static org.mockito.Mockito.*; public class FooBusinessLogicTest { /** * isAnyConditionがtrueを返し、doSomethingが呼ばれることを検証する。 */ @Test public void testExecuteDoSomethingInvoked() { FooBusinessLogic logic = spy(new FooBusinessLogic()); // isAnyConditionの中身を実行せず、強制的にtrueを返す doReturn(true).when(logic).isAnyCondition(); // doSomethingが呼ばれても何もしない doNothing().when(logic).doSomething(); // doOtherthingが呼ばれたらアサーションエラー doThrow(new org.junit.AssertionFailedError()).when(logic).doOtherthing(); logic.execute(); // doSomethingが一度だけ呼ばれたことを確認 verify(logic, times(1)).doSomething(); } }
Mockito#spyのスゴイところは、具象クラスのメソッドのうち、
挙動を入れ替えたいメソッドだけを入れ替えられるということ。
例えばEasyMockだと、
FooBusinessLogic mockLogic = EasyMock.createMock(FooBusinessLogic.class); // isAnyConditionがtrueを返すように仕向ける EasyMock.expect(mockLogic.isAnyCondition()).andReturn(true); // doSomethingが呼び出されても何もしないように仕向ける mockLogic.doSomething(); EasyMock.expectLastCall(); // テスト実行…? mockLogic.execute(); EasyMock.replay();
などと書けば行けそうに見えますが、これはダメです。
なぜならmockLogicは完全に元の振る舞いを失っており、中身は空っぽだから。
mockLogic.executeを呼び出してもEasyMockのエラーになって終わるだけなのです。
今日から君もMockito信者
さあ乗り換えるんだ今すぐに。NOW!