【问题标题】:Mockito fails to mock Rest API callMockito 无法模拟 Rest API 调用
【发布时间】:2019-10-03 15:32:12
【问题描述】:

使用 Mockito 测试 REST API,总是会出错。我试图模拟

apiClient.execute(buildApiCall("POST", url, reqBody), localVarReturnType);

但是没有任何效果,这里的任何帮助将不胜感激

被测系统

public CreateUser createUser(String fullname, String username, String password, String departmentName) {
    String url = "/api/v1/account/?apikey=" + apiKey;
    String randomUsername = generateRandomString(20);
    CreateUser payload = new CreateUser();
    payload.setFullName(fullname);
    payload.setUsername(username);
    payload.setPassword(generatePassword((password != null) ? password : generatePassword(randomUsername)));
    payload.setEmail(username + "@qstack.com");
    payload.setDepartmentName(departmentName);

    RequestBody reqBody = apiClient.serialize(payload, "application/json");
    Type localVarReturnType = new TypeToken<CreateUser>() { }.getType();
    ApiResponse<CreateUser> responseFromApiClient = apiClient.execute(buildApiCall("POST", url, reqBody), localVarReturnType);
    return responseFromApiClient.getData();
}

public Call buildApiCall(String method, String url, RequestBody reqBody) {
    String signature = null;
    try {
        signature = generateSignature(url);
    } catch (SignatureException e) {
        e.printStackTrace();
    }

    return apiCall(method, url, signature, reqBody);
}

private Call apiCall(String method, String url, String signature, RequestBody reqBody) {
    String baseUri = "http://localhost:8002";
    String finalUrl = baseUri + url + "&signature=" + signature;
    final Request.Builder reqBuilder = new Request.Builder().url(finalUrl);
    reqBuilder.header("Accept", "*/*");
    reqBuilder.header("Content-Type", "application/json");

    return httpClient.newCall(reqBuilder.method(method, reqBody).build());
}

单元测试代码

 @Test
public void testQuery() {
    String jsonResponse = "{\n" +
            "    \"address_one\": \"\",\n" +
            "    \"address_three\": \"\",\n" +
            "    \"address_two\": \"\",\n" +
            "    \"country_code\": \"\",\n" +
            "    \"cs_domain_id\": null,\n" +
            "    \"date_joined\": \"2019-10-02T19:12:55.722764\",\n" +
            "    \"disable_personal_account\": false,\n" +
            "    \"email\": \"username123@qstack.com\",\n" +
            "    \"first_name\": \"username123\",\n" +
            "    \"id\": 73,\n" +
            "    \"is_reseller\": false,\n" +
            "    \"is_superuser\": false,\n" +
            "    \"is_verified\": true,\n" +
            "    \"last_login\": null,\n" +
            "    \"last_name\": \"\",\n" +
            "    \"paywall\": true,\n" +
            "    \"postal_code\": \"\",\n" +
            "    \"projects\": [],\n" +
            "    \"reference_number\": \"\",\n" +
            "    \"reseller_id\": 1,\n" +
            "    \"reseller_name\": \"ROOT\",\n" +
            "    \"reseller_rights\": [],\n" +
            "    \"resource_uri\": \"/api/v1/account/73/\",\n" +
            "    \"saml\": false,\n" +
            "    \"state\": 0,\n" +
            "    \"status\": \"enabled\",\n" +
            "    \"surname\": \"\",\n" +
            "    \"username\": \"username123\",\n" +
            "    \"uuid\": \"9f79e45f-9cc8-40e8-b5c6-bd406893bdd6\"\n" +
            "}";

    Gson gson = new GsonBuilder().create();
    CreateUser response = gson.fromJson(jsonResponse, CreateUser.class);
    ApiResponse<CreateUser> responseFromApiClient = new ApiResponse<>(200, null, response);

    OkHttpClient httpClientMock = mock(OkHttpClient.class);
    ApiClient apiClientMock = mock(ApiClient.class);

    when(apiClientMock.serialize(payload, "application/json")).thenReturn(any());
    GqConsoleApiClient g = new GqConsoleApiClient(httpClientMock, apiClientMock);
    Mockito.<ApiResponse<? extends CreateUser>>when(apiClientMock.execute(any(), any())).thenReturn(responseFromApiClient);
    g.createUser("jon", "jon", "jon", "ROOT");

    Assertions.assertEquals("jon", responseFromApiClient.getData().getUsername());
}

在查看 IntelliJ 的覆盖率输出时,我设法到达倒数第二行

ApiResponse responseFromApiClient = apiClient.execute(buildApiCall("POST", url, reqBody), localVarReturnType);

但测试抛出异常

java.lang.IllegalArgumentException: method POST must have a request body.

at com.squareup.okhttp.Request$Builder.method(Request.java:259)
at com.greenqloud.netappstats.collector.metrics.gqconsole.api.GqConsoleApiClient.apiCall(GqConsoleApiClient.java:136)
at com.greenqloud.netappstats.collector.metrics.gqconsole.api.GqConsoleApiClient.buildApiCall(GqConsoleApiClient.java:126)
at com.greenqloud.netappstats.collector.metrics.gqconsole.api.GqConsoleApiClient.createUser(GqConsoleApiClient.java:82)
at com.greenqloud.netappstats.collector.metrics.gqconsole.api.MockitoTest.testQuery(MockitoTest.java:87)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:513)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:170)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:166)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:113)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:113)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:121)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:121)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:121)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:121)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:55)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Edited new error 添加新的模拟调用后,请参阅test func.

at io.swagger.cv.client.ApiClient.execute(ApiClient.java:840)
at com.greenqloud.netappstats.collector.metrics.gqconsole.api.MockitoTest.testQuery(MockitoTest.java:106)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:513)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:170)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:166)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:113)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:113)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:121)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)

这个错误指向这个函数

public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiException {
    try {
        Response response = call.execute();
        T data = handleResponse(response, returnType);
        return new ApiResponse<T>(response.code(), response.headers().toMultimap(), data);
    } catch (IOException e) {
        throw new ApiException(e);
    }
}

【问题讨论】:

  • buildApiCall 方法内部发生了什么?
  • @Sneh 按要求将buildApiCall 添加到问题中
  • 异常告诉你发生了什么。 Request.Builder 在进行 POST 时需要的主体
  • 但是我在模拟这个函数,那么不应该处理所有参数输入问题吗?

标签: java unit-testing mockito


【解决方案1】:

你不是在嘲笑这个电话

apiClient.serialize(payload, "application/json");

因此可能返回null

不要忘记 apiClient 是一个模拟

ApiClient apiClientMock = mock(ApiClient.class);

而你只是在嘲笑execute 方法。

Mockito.<ApiResponse<? extends CreateUser>>when(apiClientMock.execute(any(), any())).thenReturn(responseFromApiClient);

通过模拟 serialize 方法来解决此问题。

【讨论】:

  • 是否需要模拟 apiClient.serialize(payload, "application/json"); 它具有正确执行所需的所有参数?
  • 自己试试看调用结果。 apiClient 是一个模拟,因此没有真正调用 serialize 方法。那么这个调用会返回什么呢?
  • 试图模拟 apiClient.serializelike this when(apiClientMock.serialize(payload, "application/json")).thenReturn(any()); 但抛出了一个不同的错误:请参阅已编辑的原始问题。
  • 您必须按照代码的顺序来查看在实际代码中使用模拟的位置。您正在返回 any(),那么在 buildApiCall 中使用 reqBody 时会发生什么?你还在嘲笑httpClient吗?另一点是你现在面临一个新问题,不应该用另一个新问题来编辑你原来的问题..
  • 我和这个问题斗争了很长一段时间,老实说,不认为这是值得的麻烦!我似乎无法从 apiClient.serialize 函数返回一些东西。它返回RequestBody,我无法手动创建。此外,我似乎需要与buildApiCall() 进行一些交互,并且作为类函数,我可能不应该事先嘲笑它?
猜你喜欢
  • 1970-01-01
  • 2015-11-04
  • 1970-01-01
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多