1

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?

4

1 回答 1

3

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.

我不确定你是否可以用 JMockit 做你想做的事情。这是一种不太动态的方法来做你想要实现的目标。在我自己的测试用例的集合中需要一些可注入模拟的地方,我采用了这种方法。我发现验证行为已经足够了;我很欣赏它可能无法处理更复杂的测试用例。

@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());
    }
}

我意识到我可能会说显而易见的,但在上面的模拟测试中,您的测试只是验证您的测试用例配置,没有实际的测试代码。

于 2013-10-17T17:14:00.830 回答