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 并非不可能。但是你原来的问题是 AppointmentClientFactorystatic 方法。您绝对应该将此方法更改为实例方法(至少为了更好的架构),例如:

 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。