如何将部分模拟从 mockito 转换为 JMockit?



我有一些使用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 将在下一行中的所有结构体创建模拟。

相关内容

  • 没有找到相关文章

最新更新