我有一些使用Mockito的测试代码(见下文),但是,我认为我需要切换到更强大的JMockit才能完成我的测试。
但是,如果我在 Session 类上使用@Tested
注释,则从构造函数调用的 init 方法会引发异常。
如果我在 Session 类上使用@Mocked
,我没有可用于测试的真正方法。
下面没有显示的是,在测试期间,我还需要访问details
对象。但是,这是在构造函数期间从plugin
对象检索的,构造函数调用有问题的init
方法。
如何使用JMockit测试checkDetails
方法?
测试代码
import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;
public class SessionTest {
@Test
public void testSession() {
Session session = Mockito.mock(Session.class);
Mockito.doCallRealMethod().when(session).checkDetails(Mockito.any(String.class));
Assert.assertEquals(session.checkDetails("Foo"), true);
}
}
要测试的类
import java.net.Socket;
import java.net.SocketException;
import java.util.List;
public class Session {
private final Socket socket;
private final List<String> details;
private final Plugin plugin;
public Session(Socket socket, Plugin plugin) throws SocketException {
this.socket = socket;
this.plugin = plugin;
this.details = plugin.getDetails();
init();
}
public void init() throws SocketException {
socket.setTcpNoDelay(true);
}
public boolean checkDetails(String item) {
for (String detail : details) {
if (detail.equals(item)) {
return true;
}
}
return false;
}
}
屬地
import java.util.List;
public class Plugin {
private List<String> details;
public List<String> getDetails() {
return details;
}
}
注意:这是一个非常简化的代码版本,目前无法更改生产代码。
我个人不会在部分嘲笑的情况下运行此测试。这不是您希望部分模拟待测试代码的情况。(我只在我有一个独立测试的实用程序方法的情况下嘲笑 CUT,并且我想假设实用程序方法工作得很好,并测试更大的上下文。你在这里没有这样做。
我会采取两种策略之一,这取决于我的目标是什么。
选项一
将构造函数视为要测试的一部分:
@RunWith(JMockit.class)
public class SessionTest {
@Injectable Socket socket;
@Injectable Plugin plugin;
@Test
public void testCheckDetails() throws Exception {
new Expectations() {{
plugin.getDetails(); result = Arrays.asList("Foo", "Bar", "Baz");
}};
Session session = new Session(socket, plugin);
boolean b = session.checkDetails("Foo");
assertThat(b, is(true));
}
}
备选方案二
假设对象已正确构造,并且其List<String> details
字段已正确初始化。
@RunWith(JMockit.class)
public class SessionTest {
@Tested
Session cut;
@Injectable Socket socket;
@Injectable Plugin plugin;
@Test
public void testCheckDetails() throws Exception {
List<String> details = Arrays.asList("Foo", "Bar", "Baz");
Deencapsulation.setField(cut, details);
boolean b = cut.checkDetails("Foo");
assertThat(b, is(true));
}
}
实际部分模拟
如果这不能解决问题,并且您真的一心想进行部分嘲笑(例如,您还没有给出完整的故事,您真的需要这样做),那么这就是它的工作原理。假设我们在Session
上还有一种方法:
public List<String> getDetails() {
return plugin.getDetails();
}
让我们进一步假设checkDetails
查询此帮助程序方法,而不是实例中的字段。
public boolean checkDetails(String item) {
for (String detail : getDetails()) {
if (detail.equals(item)) {
return true;
}
}
}
那么使用部分模拟的测试可能如下所示:
@RunWith(JMockit.class)
public class SessionTest {
@Tested
Session cut;
@Injectable Socket socket;
@Injectable Plugin plugin;
@Test
public void testCheckDetails() throws Exception {
new Expectations(cut) {
{
cut.getDetails(); result=Arrays.asList("Foo", "Bar", "Baz");
}
};
boolean b = cut.checkDetails("Foo");
assertThat(b, is(true));
}
}
假装
或者,您可以使用假货。同样,getDetails()
方法的假设与上一节相同:
@RunWith(JMockit.class)
public class SessionTest {
@Tested
Session cut;
@Injectable Socket socket;
@Injectable Plugin plugin;
@Test
public void testCheckDetails() throws Exception {
new MockUp<Session>() {
@Mock
List<String> getDetails() {
return Arrays.asList("Foo", "Bar", "Baz");
}
};
Session cut = new Session(socket, plugin);
boolean b = cut.checkDetails("Foo");
assertThat(b, is(true));
}
}
结语
请注意,在所有情况下,当您开始部分模拟或伪造对象时,默认操作是对该部分模拟或伪造进行实际实现。只有明确嘲笑/伪造的方法才会被嘲笑或伪造。这与你在有显式doCallRealMethod()
时使用的 Mockito 方法相反(如果没有它,您将调用模拟方法)。
另外,assertThat(...)
的东西不是JMockit的一部分;它是JUnit的一部分,正是我喜欢做测试的方式,我懒得回去把它改成你的断言。
JMockit 等效于Session session = Mockito.mock(Session.class);
是
Session session = new MockUp<Session>() {}.getMockInstance();
或
@Mock
Session session;
正如@dcsohl的回答中提到的,没有直接等同于Mockito.doCallRealMethod().when(session).checkDetails(Mockito.any(String.class));
但是,您可以使用MockUp
方法使用所有实际方法,然后指定要排除的方法。在这种情况下,init
方法是嘲笑的主要原因,也是我们需要从调用中排除的内容。
new MockUp<Session>() {
@Mock
public void init() {
//Don't call the real init
}
};
session = new Session(mockPlugin, mockSocket);
请注意,在这种情况下,我们不会在模型上调用getMockInstance
。但是,JMockit 将在下一行中的所有结构体创建模拟。