Mockito を使っていたときは、モック化されたインスタンスを簡単に作成できました。しかし、JMockit ではそれほど単純ではないようです。私の考えを説明するために、次の例を使用しましょう。
public class App {
private String name;
public App(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
これは非常に単純な不変のラッパーです。Mockito を使用してテストするには、次のようなコードを記述できます。
List<App> mockApps = new ArrayList<App>();
String[] fakeNames = new String[] {"a", "b", "c"};
for (String name : fakeNames) {
// create a mocked instance
// I don't care if the class has a default ctor or not
App mockApp = mock(App);
when(mockApp.getName()).thenReturn(name);
// add to the container
mockApps.add(mockApp);
}
// assertions
for (int i = 0; i < fakeNames.length; i++) {
assertThat(mockApps.get(i).getName(), is(fakeNames[i]));
}
ただし、JMockit では状況が変わります: (何か見落としているかもしれません)
同じ結果を得るには、次のことを行う必要があります。
@Mocked App app; // let JMockit kick in for this class
List<App> mockApps = new ArrayList<App>();
String[] fakeNames = new String[] {"a", "b", "c"};
for (final String name : fakeNames) {
// create a mocked instance
// DIFFERENCE: I have to use the ctor provided by App class
// I actually can just pass "name" to the ctor here in this example
// but let's assume getName() in reality has much more complex logic
App mockApp = new App(null);
new NonStrictExpectations(mockApp) {
mockApp.getName(); result = name;
}
mockApps.add(mockApp);
}
// assertions
for (int i = 0; i < fakeNames.length; i++) {
assertThat(mockApps.get(i).getName(), is(fakeNames[i]));
}
それが正しいアプローチかどうかはわかりませんが、うまくいきます。そして、これは行動ベースのテストと呼ばれていると思います。
質問 1: ctor をバイパスできますか? (単純にすべてのヌルを渡すことができるようですが、そうしたくありません。)
JMockit の状態ベースのテストに関して別の質問があります。
インスタンスを達成するためにこの方法を行う場合:
List<App> mockApps = new ArrayList<App>();
String[] fakeNames = new String[] {"a", "b", "c"};
for (final String name : fakeNames) {
new MockUp<App> {
@Mock
public String getName() {
return name;
}
}
App mockApp = new App(null);
mockApps.add(mockApp);
}
すべての mockApp がその名前として「c」を返すため、事態はさらに悪化します。実行時にモック化されたクラスは常に1つしか存在できず、後で定義されたものはすべて前のものに取って代わりますが、これは意味がないと思います。
質問 2 は、状態ベースのテストでさまざまなモック インスタンスを取得できますか?