java - Mockito 无法模拟 Rest API 调用
问题描述
使用 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)
添加新的模拟调用后编辑新错误,请参阅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);
}
}
解决方案
你不是在嘲笑这个电话
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
方法来解决此问题。
推荐阅读
- python - 修改第一个条时如何获得条形图的正确图例?
- python - PYTHON:使用套接字和 VM 进行连接检查
- python - Groupby 按列值的条件选择行,然后转换另一列
- javascript - d3.js 可刷的 choropleth
- java - Kotlin 中的线程同步是如何工作的?
- asp.net-core - 即使更改了连接字符串,Hangfire 仍然指向旧数据库
- python - 无论如何要更改熊猫中的数据框以使索引位于中间?
- angular - Routing with in the Module in Angular
- php - 寻找提示以更好地理解 Perl 兼容的正则表达式运算符和语法
- arrays - 在 VBA 中,如何修改 .txt 文件的特定行?