Mockito - 如何模拟在方法中实例化的变量?
Mockito - how to mock a variable instantiated in a method?
我有以下Java
方法:
public Appointment addAppointment(String client, Appointment appointment) {
String esbUrl = new ESBUrlHelper().getEsbUrl();
AppointmentClient appointmentClient = AppointmentClientFactory.getUnsecuredClient(esbUrl);
if (appointment.getId() == null) {
outputAppointment = appointmentClient.addAppointment(client, appointment);
}
return outputAppointment;
}
上述方法调用第三方 REST
客户端 appointmentClient
。
我遇到的问题是这导致我的测试失败。
如何在我的单元测试中模拟 appointmentClient
对象?
目前我的测试如下:
@Test
public void shouldAddAppointment() {
// act
Appointment appointment = appointmentService.addAppointment(CLIENT_STRING, appointmentMock)
// assert
assertNotNull(appointment);
}
但我在第 appointmentClient.addAppointment(client, appointment);
行收到以下错误:
org.jboss.resteasy.client.exception.ResteasyIOException: IOException
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
我想模拟如下内容:
Mockito.when(appointmentClient.addAppointment(client, appointment)).thenReturn(appointmentMock);
使用您当前的代码,模拟调用 AppointmentClientFactory#getUnsecuredClient
的唯一方法是使用 PowerMock
,因为工厂方法是静态的。这是由于您的调用代码 addAppointment
和此处的依赖项(即 AppointmentClientFactory
)之间存在硬耦合。
如果我是你,我会避免这样做,因为 PowerMock
不是进行测试的最佳方式。相反,我会做的是将 AppointmentClientFactory
作为依赖项注入,从而允许我在测试期间模拟它的一个实例。
这应该是双重方式的最佳方法。首先,因为您实现的代码耦合度较低,其次,因为您不需要使用 PowerMock
进行单元测试。
使用 Mockito 并非不可能。但是你原来的问题是 AppointmentClientFactory
的 static
方法。您绝对应该将此方法更改为实例方法(至少为了更好的架构),例如:
public class AppointmentClientFactory {
public AppointmentClient getUnsecuredClient(String url) {
return new AppointmentClient(); //your implementation
}
}
然后你的 AppointmentService 看起来像(或接近它):
public class AppointmentService {
private final AppointmentClientFactory factory;
public AppointmentService() {
this(new AppointmentClientFactory());
}
public AppointmentService(AppointmentClientFactory factory) {
this.factory = factory;
}
public Appointment addAppointment(String client, Appointment appointment) {
String esbUrl = "";
Appointment outputAppointment = null;
AppointmentClient appointmentClient = new AppointmentClientFactory().getUnsecuredClient(esbUrl);
if (appointment.getId() == null) {
outputAppointment = appointmentClient.addAppointment(client, appointment);
}
return outputAppointment;
}
}
然后你可以这样写测试:
public class AppointmentTest {
private final String CLIENT_STRING = "";
@Test
public void shouldAddAppointment() {
AppointmentClientFactory clientFactory = Mockito.mock(AppointmentClientFactory.class);
AppointmentClient mockedClient = Mockito.mock(AppointmentClient.class);
AppointmentService service = new AppointmentService(clientFactory);
Appointment appointmentMock = new Appointment();
when(clientFactory.getUnsecuredClient(any())).thenReturn(mockedClient);
Appointment appointment = service.addAppointment(CLIENT_STRING, appointmentMock);
assertNotNull(appointment);
}
}
您可以尝试为此使用 PowerMockito。
首先,您需要模拟 AppointmentClientFactory
class 的静态方法调用,如下所示:
PowerMockito.mockStatic(AppointmentClientFactory.class);
PowerMockito.when(AppointmentClientFactory,"getUnsecuredClient",esbUrl).thenReturn(appointmentClient);
此外,当您使用 PowerMockito 模拟静态方法时,将 @PrepareForTest({AppointmentClientFactory.class})
注释添加到测试 class。
我有以下Java
方法:
public Appointment addAppointment(String client, Appointment appointment) {
String esbUrl = new ESBUrlHelper().getEsbUrl();
AppointmentClient appointmentClient = AppointmentClientFactory.getUnsecuredClient(esbUrl);
if (appointment.getId() == null) {
outputAppointment = appointmentClient.addAppointment(client, appointment);
}
return outputAppointment;
}
上述方法调用第三方 REST
客户端 appointmentClient
。
我遇到的问题是这导致我的测试失败。
如何在我的单元测试中模拟 appointmentClient
对象?
目前我的测试如下:
@Test
public void shouldAddAppointment() {
// act
Appointment appointment = appointmentService.addAppointment(CLIENT_STRING, appointmentMock)
// assert
assertNotNull(appointment);
}
但我在第 appointmentClient.addAppointment(client, appointment);
行收到以下错误:
org.jboss.resteasy.client.exception.ResteasyIOException: IOException
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
我想模拟如下内容:
Mockito.when(appointmentClient.addAppointment(client, appointment)).thenReturn(appointmentMock);
使用您当前的代码,模拟调用 AppointmentClientFactory#getUnsecuredClient
的唯一方法是使用 PowerMock
,因为工厂方法是静态的。这是由于您的调用代码 addAppointment
和此处的依赖项(即 AppointmentClientFactory
)之间存在硬耦合。
如果我是你,我会避免这样做,因为 PowerMock
不是进行测试的最佳方式。相反,我会做的是将 AppointmentClientFactory
作为依赖项注入,从而允许我在测试期间模拟它的一个实例。
这应该是双重方式的最佳方法。首先,因为您实现的代码耦合度较低,其次,因为您不需要使用 PowerMock
进行单元测试。
使用 Mockito 并非不可能。但是你原来的问题是 AppointmentClientFactory
的 static
方法。您绝对应该将此方法更改为实例方法(至少为了更好的架构),例如:
public class AppointmentClientFactory {
public AppointmentClient getUnsecuredClient(String url) {
return new AppointmentClient(); //your implementation
}
}
然后你的 AppointmentService 看起来像(或接近它):
public class AppointmentService {
private final AppointmentClientFactory factory;
public AppointmentService() {
this(new AppointmentClientFactory());
}
public AppointmentService(AppointmentClientFactory factory) {
this.factory = factory;
}
public Appointment addAppointment(String client, Appointment appointment) {
String esbUrl = "";
Appointment outputAppointment = null;
AppointmentClient appointmentClient = new AppointmentClientFactory().getUnsecuredClient(esbUrl);
if (appointment.getId() == null) {
outputAppointment = appointmentClient.addAppointment(client, appointment);
}
return outputAppointment;
}
}
然后你可以这样写测试:
public class AppointmentTest {
private final String CLIENT_STRING = "";
@Test
public void shouldAddAppointment() {
AppointmentClientFactory clientFactory = Mockito.mock(AppointmentClientFactory.class);
AppointmentClient mockedClient = Mockito.mock(AppointmentClient.class);
AppointmentService service = new AppointmentService(clientFactory);
Appointment appointmentMock = new Appointment();
when(clientFactory.getUnsecuredClient(any())).thenReturn(mockedClient);
Appointment appointment = service.addAppointment(CLIENT_STRING, appointmentMock);
assertNotNull(appointment);
}
}
您可以尝试为此使用 PowerMockito。
首先,您需要模拟 AppointmentClientFactory
class 的静态方法调用,如下所示:
PowerMockito.mockStatic(AppointmentClientFactory.class);
PowerMockito.when(AppointmentClientFactory,"getUnsecuredClient",esbUrl).thenReturn(appointmentClient);
此外,当您使用 PowerMockito 模拟静态方法时,将 @PrepareForTest({AppointmentClientFactory.class})
注释添加到测试 class。