如何嘲笑客户的来电?

时间:2019-02-10 07:15:42

标签: java unit-testing junit mockito jersey-client

这是我的代码

@Service
public class PaymentHandler {
private static final Gson GSON = new Gson();

private static Client webServiceClient = createSslClient(); // function creates a ssl connection

public Response makePayment(String payload) {
    WebResource webResource = webServiceClient.resource(url);

    WebResource.Builder builder = webResource.getRequestBuilder();

    String r = builder
            .type(MediaType.APPLICATION_JSON_TYPE)
            .accept(MediaType.APPLICATION_JSON_TYPE)
            .post(String.class, payload);

    Response response = GSON.fromJson(r, Response.class);
}
}

这是我尝试测试的方法,该方法无效,它总是会调用支付服务。我无法嘲笑它。

Client client = mock(Client.class );
WebResource webResource = mock(WebResource.class);
WebResource.Builder builder = mock(WebResource.Builder.class);
ClientResponse clientResponse = mock(ClientResponse.class);
when(client.resource(anyString())).thenReturn(webResource);
when(webResource.getRequestBuilder()).thenReturn(builder);

when(builder.type(anyString())).thenReturn(builder);
when(builder.accept(anyString())).thenReturn(builder);
when(builder.post(Matchers.eq(String.class), anyString())).thenReturn("Test");
paymentHandler.makePayment(payload); //assume that I send actual payload

有人可以告诉我如何嘲笑吗?

2 个答案:

答案 0 :(得分:2)

在您的测试中,我看不到您将webServiceClient替换为模拟版本。

但是首先,我相信最好不要在没有依赖注入的情况下编写诸如PaymentHandler这样的代码。可能只是将webServiceClient注入PaymentHandler中的简单合成。没有依赖注入,它就不灵活,难以维护,因此无法测试。例如,想象一下,如果初始化该字段需要与外部系统进行某些交互,将会发生什么。没有任何字节码操作库,您将如何进行测试?或者您如何轻松地从一个webServiceClient迁移到另一个{从非SSL改为ssl?

尽管存在这些众所周知的问题,但有时我们还是不得不处理无法轻易更改的第三方代码或旧代码。但是我们要为与该第三方代码交互的代码编写测试。出于这个确切的原因,存在一些很酷的测试框架。 PowerMock是其中之一,下面是使用它的工作代码:

@RunWith(PowerMockRunner.class)
@PrepareForTest(PaymentHandler.class)
public class PaymentHandlerTest {
    @Test
    public void test() throws Exception {
        //we don't want to initialize the PaymentHandler.class because it might cause some
        //heavy undesirable initilization. E.g. if we had referred to PaymentHandler as a class
        //literal here, then the webServiceClient would've been initializaed with some "real"
        //instance of Client. My PaymentHandler is located in so package. You should specify your
        //fully qualified class' name here
        Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass("so.PaymentHandler");

        //now the webServiceClient will be null once we initialize the PaymentHandler class
        PowerMockito.suppress(PowerMockito.method(clazz, "createSslClient"));
        Client client = mock(Client.class);

        //here we initialize the PaymentHandler.class and finally mock the webServiceClient
        Whitebox.setInternalState(clazz, "webServiceClient", client);

        PaymentHandler paymentHandler = new PaymentHandler();

        WebResource webResource = mock(WebResource.class);
        WebResource.Builder builder = mock(WebResource.Builder.class);
        when(client.resource(anyString())).thenReturn(webResource);
        when(webResource.getRequestBuilder()).thenReturn(builder);
        //take note of any(MediaType.class) instead of anyString() from your example. As in
        //your PaymentHandler, MediaType is used instead of String
        when(builder.type(any(MediaType.class))).thenReturn(builder);
        when(builder.accept(any(MediaType.class))).thenReturn(builder);
        when(builder.post(Matchers.eq(String.class), anyString())).thenReturn("{}");

        paymentHandler.makePayment("payload");
    }
}

在我的示例中,我使用了以下依赖项:

testCompile group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.0'
testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.0'

这些是最新版本,但较早的版本也可以做到

答案 1 :(得分:1)

这是我用来嘲笑的方式

@Mock
Client client;

@Mock
WebResource webResource;

@Mock
WebResource.Builder builder;


@Test
public void test() {
ReflectionTestUtils.setField(payeezyHandler,"webServiceClient",client);
Mockito.when(client.resource(anyString())).thenReturn(webResource);
Mockito.when(webResource.getRequestBuilder()).thenReturn(builder);

Mockito.when(builder.type(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builder);
Mockito.when(builder.accept(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builder);
Mockito.when(builder.post(Matchers.eq(String.class),anyString())).thenReturn(fakeResponse());
}

我知道ReflectionTestUtils不好用。但是,如果您的测试类仅具有一个要测试的公共函数,那么我想这没有什么害处。