Mockitoの真価はspyにあり (original) (raw)

f:id:takahashikzn:20101001105008j:image

以前の日記で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();

    
    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() {
    
}

protected void doSomething() {
    
}

protected void doOtherthing() {
    
}

}

import org.junit.*;

public class FooBusinessLogicTest {

isAnyConditionがtrueを返し、doSomethingが呼ばれることを検証する。

@Test
public void testExecuteDoSomethingInvoked() {

    
    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();

    
    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());

    
    doReturn(true).when(logic).isAnyCondition();
    
    
    doNothing().when(logic).doSomething();

    
    doThrow(new org.junit.AssertionFailedError()).when(logic).doOtherthing();

    logic.execute();

    
    verify(logic, times(1)).doSomething();
}

}

Mockito#spyのスゴイところは、具象クラスのメソッドのうち、
挙動を入れ替えたいメソッドだけを入れ替えられるということ。

例えばEasyMockだと、

FooBusinessLogic mockLogic = EasyMock.createMock(FooBusinessLogic.class);


EasyMock.expect(mockLogic.isAnyCondition()).andReturn(true);

mockLogic.doSomething();
EasyMock.expectLastCall();


mockLogic.execute();

EasyMock.replay();

などと書けば行けそうに見えますが、これはダメです。

なぜならmockLogicは完全に元の振る舞いを失っており、中身は空っぽだから。
mockLogic.executeを呼び出してもEasyMockのエラーになって終わるだけなのです。

今日から君もMockito信者

追記

PowerMockというのを併用すれば、EasyMockでspy相当のことが出来るようです。

しかし。。。

public static T createPartialMock(Class type, Class<? super T> where, String... methodNames)

ん〜?メソッド名を文字列で渡す必要があるのか?
これじゃEclipseリファクタリングしたときに、自動的には追従できないじゃん。

というわけでイマイチな感じです。