如何在JMockit中初始化独立的模拟实例

时间:2013-10-16 20:39:55

标签: java unit-testing jmockit

当我使用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);
}

事情变得更糟,因为所有mockApps都以“c”命名。似乎在运行时总是只有一个模拟类,以后定义的任何类都将取代以前的类,这是我认为没有意义的。

所以问题2是:我可以在基于状态的测试中获得不同的模拟实例吗?

1 个答案:

答案 0 :(得分:3)

在JMockit中,@ Injectable用于创建单个模拟实例。然后可以记录每个模拟实例的特定行为。

在这两个问题中,您正在采用可能(或可能不是)Mockito方法来测试案例。一个不同的例子可能有助于理解您试图解决的问题。

我不确定你是否能用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());
    }
}

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