mockitoとJMockitについて簡単にまとめました。両者ともJavaのユニットテスト向けモックフレームワークなのですが、使い方は全然違います。さわりをちょっと調べただけです。有識者のコメント歓迎します。
2022-02-25 更新:JMockitは、活動を停止しているようです。
対比
mockito | JMockit | |
---|---|---|
URL | https://site.mockito.org/ | http://jmockit.github.io/ |
パッケージ名 | org.mockito.* | mockit.* |
API | https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html | 見当たらず |
プロダクト名、パッケージ名ともに紛らわしいので検索するときは注意。
コードサンプル
Logic.java
がCalc.java
を呼んでいて、CalcをモックしてLogicの動作を確認したいとします。
mockitoでのサンプル
package org.example;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.when;
public class LogicTestInMockito {
@Mock
Calc calc;
@InjectMocks
Logic logic;
@Before
public void before() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testAsUsual() throws Exception {
logic.calc = new Calc();
int[] result = logic.getDivisibleNumbers();
assertTrue(result[0] % result[1] == 0);
}
@Test
public void testInMockito() throws Exception {
when(calc.remainder(anyInt(), anyInt())).thenAnswer(i -> {
// 公式ドキュメントには書いてありませんが、ここはラムダが使えます。
return ((Integer) i.getArguments()[1]) == 5 ? 0 : 1;
});
int[] result = logic.getDivisibleNumbers();
assertTrue(result[1] == 5);
}
}
JMockitでのサンプル (Expectations APIでの例)
package org.example;
import mockit.Expectations;
import mockit.Mocked;
import mockit.Verifications;
import mockit.integration.junit4.JMockit;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertTrue;
@RunWith(JMockit.class)
public class LogicTestInJMockit {
@Test
public void testAsUsual() throws Exception {
Logic logic = new Logic(new Calc());
int[] result = logic.getDivisibleNumbers();
assertTrue(result[0] % result[1] == 0);
}
@Test
public void testInJMockit(@Mocked Calc calc) throws Exception {
new Expectations() {{
calc.remainder(anyInt, 5);
result = 0;
calc.remainder(anyInt, anyInt);
result = 1;
}};
Logic logic = new Logic(calc);
int[] result = logic.getDivisibleNumbers();
new Verifications() {{
calc.remainder(anyInt, 5);
minTimes = 1;
}};
assertTrue(result[1] == 5);
}
}
JMockitでのサンプル (Mockups APIでの例)
package org.example;
import mockit.Mock;
import mockit.MockUp;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class LogicTestInJMockitMockups {
// このテストが、testInJMockitWithMockups() の後に実行されると
// モックしていないCalcを使っているつもりがモックされたものになってしまう様子。
@Test
public void testAsUsual() throws Exception {
Logic logic = new Logic(new Calc());
int[] result = logic.getDivisibleNumbers();
assertTrue(result[0] % result[1] == 0);
}
@Test
public void testInJMockitWithMockups() throws Exception {
new MockUp<Calc>() {
@Mock()
public int remainder(int dividend, int divisor) {
return divisor == 5 ? 0 : 1;
}
};
Logic logic = new Logic(new Calc());
int[] result = logic.getDivisibleNumbers();
assertTrue(result[1] == 5);
}
}
モック対象のコード
package org.example;
public class Calc {
int remainder(int dividend, int divisor) {
return dividend % divisor;
}
}
テスト対象のコード
package org.example;
public class Logic {
Calc calc;
Logic(Calc calc) {
this.calc = calc;
}
/**割り切れる数字のペアをアトランダムに返す。*/
int[] getDivisibleNumbers() {
while (true) {
int candidate = (int) (Math.random() * 100) + 1;
int candidate2 = (int) (Math.random() * 10) + 1;
if (calc.remainder(candidate, candidate2) == 0) {
return new int[]{candidate, candidate2};
}
}
}
}
寸評
mockitoはモックの挙動を when()
doReturn()
thenAnswer()
で決めていくスタイル。 チェーン表記が綺麗とも煩くなるとも言われている様子。素朴ですがたくさんやると限界があるかもしれません。
JMockitはAPIにクセがあるが、慣れればOKという記事をちらほら見つかります。(なにげなくインスタンスイニシャライザ使ってて慣れるのに時間がかかるひとはいそう。)個人的には Mockups APIの場合、モックするメソッドに対してコンパイラのチェックがかからないことや、new MockUp<Calc>(){...}
した後は、モックしていないCalc
が使えなくなるなど、学習曲線がちょっと厳しいと思われました。先の例だとLogicTestInJMockitMockups.java
は、テストメソッドの順序次第でtestAsUsual()
が失敗します。また、mockitoよりも呼び出し回数のテストとか実引数をモックが取得できるとか細かい機能が充実していそうな様子です。(mockit.Invocation
mockit.Mock#invocations
など。)
私の結論
学習曲線が低そう。ラムダが使えて幸せな気持ちになれそう。ロゴのデザインが良い。などという薄っぺらい理由でとりあえずmockitoを使ってみようと思います。限界を感じたら JMockitも深耕してみようかなと。
参考リンク
見つかった比較記事は両方とも JMockit 押し。ただし2013年とかちょっと古い。多分このネタ自体は既にhotではなくて、両者それなりに使われているのではないかと想像する次第。
バージョン番号など
- mockito 1.10.19
- JMockit 1.21
- Java 8