Reputation: 2549
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?
Upvotes: 1
Views: 5109
Reputation: 136
In JMockit @Injectable is used to create individual mocked instances. It is then possible to record specific behavior for each mocked instance.
In both questions you are taking what may (or may not) be a Mockito approach to a test case. A different example may help in understanding the problem you are trying to solve.
I am not sure if you can do what you are trying to with JMockit. Here is a less dynamic method of doing what you are trying to achieve. Where I have needed some injectable mocks in a collection in my own test cases I've taken this approach. I've found that it has been adequate to verify behavior; I appreciate it may not deal with a more complicated test case.
@Injectable App app1;
@Injectable App app2;
@Test
public void testApps() throws Exception {
final App [] apps = new App[2];
final String [] names = {"a", "b"};
apps[0] = app1;
apps[1] = app2;
new NonStrictExpectations() {
{
app1.getName();
result = "a";
app2.getName();
result = "b";
}
};
for (int i = 0; i < apps.length; i++){
System.out.println(apps[i].getName());
}
}
I realise I may be stating the obvious, but in the mockito test above your test is simply verifying your test case configuration and no actual test code.
Upvotes: 3