当我使用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是:我可以在基于状态的测试中获得不同的模拟实例吗?
答案 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());
}
}
我意识到我可能会说明显而易见的,但在上面的模拟测试中,测试只是验证您的测试用例配置而不是实际的测试代码。