模拟OkHttp响应

18

我有下面的方法,它会执行GET请求3次直到成功为止。

如何更好地模拟这个方法?我想模拟和测试状态码401、状态码500,并测试该方法是否执行了三次。

在Python中,我们有https://github.com/getsentry/responses ,它直接模拟请求,因此很容易测试这些方法。

在Java中有类似的东西吗?

@Override
    public <T> UResponse<T> get(Request request, JSONUnmarshaler<T> unmarshaller, Gson gson) throws UException {
        int status_code = 0;
        String next = null;
        String rawJSON = null;
        JsonElement jsonelement = null;
        Boolean retry = true;
        try {
            int attempts = 3;
            while ((attempts >= 0)  && (retry) && status_code != 200){
                Response response = this.client.newCall(request).execute();

                rawJSON = response.body().string();

                jsonelement = gson.fromJson(rawJSON, JsonElement.class);

                next = gson.fromJson(jsonelement.getAsJsonObject().get("next"), String.class);

                status_code = response.code();

                if (status_code == 401) {
                    try {
                        logger.warn("token expired");
                        TimeUnit.SECONDS.sleep(5);
                        retry = true;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                if ((status_code / 100 ) == 5){
                    logger.warn("gateway error");
                    retry = true;                   
                }
                attempts -= 1;
                retry = false;

            }
            if (status_code != 200){
                throw new UException();
            }
            return new UResponse<T>(status_code, next, rawJSON,
                    unmarshaller.fromJSON(gson, jsonelement.getAsJsonObject()));

        } catch (IOException e) {
            e.printStackTrace();
            throw new UException();

        }

你可以使用mockito、jmockit等工具来模拟client.newCall(request).execute()调用,以返回预期的响应并检查调用次数。或者更简单的方法是使用okhttp-client-mock(MIT),设置你的规则为3次,并检查它是否被消耗 - Morfic
或者使用 mock-server - Morfic
@Morfic 谢谢,这真的很有帮助。 - user1050619
2
没问题,如果你解决了问题,随时可以发布答案,这样其他人也能受益。干杯! - Morfic
3个回答

29

由于其循环结构,此处的测试可能较为困难。一种解决方案是将this.client.newCall(request).execute()拆分成一个单独的函数sendRequest(Request request, int attemptsRemaining)。然后,您可以使用间谍(spy)来存根(stub)该方法,以便根据剩余尝试次数返回不同的响应。

我意识到 okhttp 的好处在于您可以自己构建虚假响应。例如,

Request mockRequest = new Request.Builder()
                .url("https://some-url.com")
                .build();
return new Response.Builder()
                .request(mockRequest)
                .protocol(Protocol.HTTP_2)
                .code(401) // status code
                .message("")
                .body(ResponseBody.create(
                        MediaType.get("application/json; charset=utf-8"),
                        "{}"
                ))
                .build();

因此,您可以创建并存根返回响应的简单方法,从而可以进行测试。


1
谢谢。我不知道你也可以将请求传递给响应生成器。 - Kishu Agarwal
我想模拟响应以在调用response.body().byteStream()时抛出异常。我该怎么做? - techBeginner

24

你可以在依赖项列表中添加一些嘲笑框架,这都很好,但是,我比较传统,更喜欢只模拟remoteCallexecute方法:

将此方法复制到您的测试类中:

private static OkHttpClient mockHttpClient(final String serializedBody) throws IOException {
        final OkHttpClient okHttpClient = mock(OkHttpClient.class);

        final Call remoteCall = mock(Call.class);

        final Response response = new Response.Builder()
                .request(new Request.Builder().url("http://url.com").build())
                .protocol(Protocol.HTTP_1_1)
                .code(200).message("").body(
                   ResponseBody.create(
                        MediaType.parse("application/json"),
                        serializedBody
                ))
                .build();

        when(remoteCall.execute()).thenReturn(response);
        when(okHttpClient.newCall(any())).thenReturn(remoteCall);

        return okHttpClient;
    }

用法:

final OkHttpClient mockedClient = mockHttpClient("{\"key\": \"val\"}");

1
为了修复已弃用的ResponseBody.create()使用: ResponseBody.Companion.create( serializedBody, MediaType.parse("application/json") )) - vhamon
太好了,谢谢!但是现在ResponseBody.create()的参数需要交换顺序——原始提供的顺序(mediaType, serializedBody)已经被弃用,现在必须是(serializedBody, mediaType)。参考:https://github.com/square/okhttp/issues/6655#issuecomment-828355734 - Tina

-2
@Test
fun verifyFailureSingleEventListener() = runBlocking {
    val sender = createSender(webServer.url("/").toString())

    webServer.enqueue(MockResponse().setResponseCode(400))
    val testEvent1 = mutableMapOf("Click" to "Open")
    sender.sendSingleEvent(testEvent1)

    assertEquals("Failure", eventListener.getStatus())
    eventListener.resetStatus()
}

我们可以设置模拟响应为400状态码或其他状态码来模拟响应


这非常不清楚。createSender(String) 函数是做什么的?它的返回类型是什么?同样的问题也出现在 evenListener 上,因为它没有被定义过。 - Chisko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接