When I was using Mockito, I can create a mocked instance easily. But it doesn't seem to be that straightforward in JMockit. To illustrate my idea, let's use this example:
public class App {
private String name;
public App(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
This is just a very simple immutable wrapper. To test it using Mockito, I can write code like this:
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]));
}
However in JMockit, things get changed: (I might miss something)
To get the same result I have to do:
@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]));
}
I'm not sure if it's the right approach but it works. And I think this is called the behavior-based test.
Question 1: Can I bypass the ctor? (It seems I can simply pass all nulls but I don't even want to do that.)
There is another question regarding the state-based test in JMockit:
If I do this way to achieve instances:
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);
}
Things get worse as all mockApps return "c" as its name. It seems that there can always be only one mocked class at runtime and whatever defined later will replace the former ones, which doesn't make sense I think.
So Question 2 is: Can I get different mocked instances in state-based test?