java - 使用 mockmvc 发出单元测试 RestController 发布请求。它在调试时正确执行请求,但为什么模拟响应为空?
问题描述
我对测试有点陌生,目前在使用MockMvc对 Spring RestController进行单元测试时遇到问题。当我执行发布请求以使用 requestBody添加新用户并且我获得请求已成功执行的 http 状态时,问题就会出现,但是一旦我将响应从 MvcResult 转换为字符串,它就会出现一个空字符串。
我调试了测试并检查了请求是否已执行,并且请求正文已成功获取并已通过所有验证,因此按照这个逻辑,我的想法是问题出在 Mockito 上,但我不太明白为什么。有人可以指点我做错了什么吗?
这是我的实体用户:
@Entity
@Table(name ="users")
public class User {
@Id
@GeneratedValue(generator= "UUID")
@GenericGenerator(name="UUID", strategy = "org.hibernate.id.UUIDGenerator")
@Column(name = "ID")
private String id;
@NotBlank(message = ResponseMessage.ERROR_INVALID_FIRST_NAME_INPUT)
@Column(name = "first_name")
private String firstName;
@NotBlank(message = ResponseMessage.ERROR_INVALID_LAST_NAME_INPUT)
@Column(name = "last_name")
private String lastName;
@NotBlank(message = ResponseMessage.ERROR_INVALID_EMAIL_INPUT)
@Email(message = ResponseMessage.ERROR_INVALID_EMAIL_INPUT)
@Column(name = "email")
private String email;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@NotBlank(message = ResponseMessage.ERROR_INVALID_PASSWORD_INPUT)
@Column(name = "password")
private String password;
@Column(name = "phone_number")
private String phoneNumber;
@Column(name = "address")
private String address;
@Temporal(TemporalType.DATE)
@Column(name = "registered_on")
private Date registeredDate;
// Constructors and setters and getters down here
}
这是休息控制器:
@Validated
@RestController
@RequestMapping(path = "/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/add")
public Response<Object> addUser(@Valid @RequestBody User user) {
return this.userService.addUser(user);
// other methods down here
}
}
这是我的休息控制器测试:
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private UserService userService;
private List<User> users;
void populateUsers() {
users = new ArrayList<>();
users.add(new User("test","test","test@test.com","test"));
users.add(new User("test1","test1","test1@test.com","test1"));
users.add(new User("test2","test2","test2@test.com","test2"));
}
@BeforeEach
void setUp() {
populateUsers();
}
@AfterEach
void clear() {
users.clear();
}
// other tests here. that actually work
@Test
void addUser() throws Exception {
// Arrange
Response<Object> expected = new Response<>(200, ResponseMessage.SUCCESS_SAVED_USER);
ObjectMapper objectMapper = new ObjectMapper();
User user = new User("Added","User","a@user.com","itWorks");
String content = "{" +
"\"firstName\":\"Added\"," +
"\"lastName\":\"User\"," +
"\"email\":\"a@user.com\"," +
"\"password\":\"itWorks\"" +
"}";
Mockito.when(userService.addUser(user)).thenReturn(expected);
// Act
String actual = mvc
.perform(TestHttpRequestBuilderHelper.httpPostRequestBuilder("/user/add", content))
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
// Assert
assertEquals(objectMapper.writeValueAsString(expected), actual);
}
}
我在构建 MockMvcRequestBulders 时使用了另一个帮助程序类,称为 TestHttpRequestBuilderHelper:
public class TestHttpRequestBuilderHelper {
private static HttpHeaders generateHttpHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-type", "application/json; charset=utf-8");
httpHeaders.add("Accept","application/json");
return httpHeaders;
}
// other overloaded methods here
public static MockHttpServletRequestBuilder httpPostRequestBuilder(String url, String content) {
return MockMvcRequestBuilders.post(url).headers(generateHttpHeaders()).content(content);
}
}
这是日志输出:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /user/add
Parameters = {}
Headers = [Content-Type:"application/json; charset=utf-8", Accept:"application/json", Content-Length:"81"]
Body = {"firstName":"Added","lastName":"User","email":"a@user.com","password":"itWorks"}
Session Attrs = {}
Handler:
Type = com.bibliotek.library.user.UserController
Method = com.bibliotek.library.user.UserController#addUser(User)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
org.opentest4j.AssertionFailedError:
Expected :{"httpCode":200,"message":"User was added successfully"}
Actual :
<Click to see difference>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1124)
at com.bibliotek.library.user.UserControllerTest.addUser(UserControllerTest.java:116)
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:564)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
2020-12-07 11:33:43.380 INFO 16288 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
Process finished with exit code -1
所以要成功,它必须是这样的:
exptected: {"httpCode":200,"message":"User was added successfully"}
actual : {"httpCode":200,"message":"User was added successfully"}
但就我而言,它的回归:
expected: {"httpCode":200,"message":"User was added successfully"}
actual: ""
解决方案
推荐阅读
- nginx - 如何访问 nginx 入口控制器
- ios - 连接蓝牙耳机时无法切换到扬声器输出
- python - 检查字典项目平均值的函数
- azure - 在 Azure ML 上自定义 Python 脚本
- c# - 如何将应用程序服务注入 AuthenticationHandler
- c++ - 如何将唯一指针从一个向量移动到另一个唯一指针向量?
- c++ - 如何通过在 QTableView 中排序来对 QListView 进行排序?
- javascript - Jinja 通过“SyntaxError: expected property name, got '%'” 错误
- http - 如何为 java servlet 应用程序设置 https
- reactjs - react-spring Transition不会动画进入状态