Mockitoの真価はspyにあり (original) (raw)
以前の日記で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でリファクタリングしたときに、自動的には追従できないじゃん。
というわけでイマイチな感じです。