英文:
Java - Incorrect output for response JSON in unit test
问题
In your unit test, the issue seems to be related to the JSON representation of the UserAmountDTO
object. Instead of returning the expected JSON representation, it's returning an empty string, which is causing the test to fail.
One potential reason for this behavior could be the usage of @JsonProperty(access = Access.WRITE_ONLY)
on the amount
field. This annotation is likely preventing the amount
field from being included in the JSON representation when serializing the object, even if you set its value in your test.
To resolve this issue, you might consider removing @JsonProperty(access = Access.WRITE_ONLY)
from the amount
field in your UserAmountDTO
class. This should ensure that the amount
field is included in the JSON representation when you serialize the object in your test.
Here's the modified UserAmountDTO
class without the @JsonProperty
annotation:
@Getter
@Setter
@AllArgsConstructor
public class UserAmountDTO {
public double amount;
@JsonProperty(access = Access.READ_ONLY)
public double balance;
}
With this change, the amount
field should be included in the JSON representation when you serialize userAmountDTO
in your test, and the test should work as expected.
英文:
I am testing my depositUserBalance endpoint controller and this is just a simple test but I am not sure why it is not getting the correct response JSON as compared to the one returned in my REST API.
// My Controller
@PutMapping("/api/deposit")
public ResponseEntity<UserAmountDTO> depositUserBalance(HttpServletRequest request,
@RequestBody UserAmountDTO userAmountDTO) {
Integer userId = (Integer) request.getAttribute("userId");
userAmountDTO = userService.updateUserBalance(userId, userAmountDTO);
return new ResponseEntity<>(userAmountDTO, HttpStatus.OK);
}
// My UserAmountDTO Data Transfer Object
@Getter
@Setter
@AllArgsConstructor
public class UserAmountDTO {
@JsonProperty(access = Access.WRITE_ONLY)
public double amount;
@JsonProperty(access = Access.READ_ONLY)
public double balance;
}
@Test
public void Test_DepositUserBalance() throws Exception {
// Arrange
UserAmountDTO userAmountDTO = new UserAmountDTO(2000.0, 3000.0);
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getAttribute("userId")).thenReturn(1);
when(userService.updateUserBalance(1, userAmountDTO)).thenReturn(userAmountDTO);
String requestBody = objectMapper.writeValueAsString(userAmountDTO);
System.out.println("requestBody: " + requestBody);
// Act
MvcResult mvcResult = mockMvc.perform(put(DEPOSIT_PATH)
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody).with(requestBuilder -> {
requestBuilder.setAttribute("userId", 1);
return requestBuilder;
}))
.andExpect(status().isOk())
.andReturn();
// Assert
int status = mvcResult.getResponse().getStatus();
String content = mvcResult.getResponse().getContentAsString();
String expectedJson = String.format("{\"balance\":%.1f}", 3000.0);
assertEquals(HttpStatus.OK.value(), status);
assertEquals(expectedJson, content);
}
In my unit test, the requestBody was returned as balance here requestBody: {"balance":3000.0}
when it should be amount because I set the @JsonProperty(acccess = Access.WRITE_ONLY)
to amount.
The error from the test was org.opentest4j.AssertionFailedError: expected: [{"balance":3000.0}] but was: []
. Why is it an empty string? When I tried it in the REST API on POSTMAN, it actually returned me this JSON balance amount. Coupled with the fact that I added @JsonProperty(acccess = Access.READ_ONLY)
to balance.
答案1
得分: 1
关于REST控制器上的单元测试POST请求的更新
我成功解决了错误,简而言之,这是因为我向模拟的服务传递了一个不同的内存地址的对象。简单地说,when(userService.updateUserBalance(1, userAmountDTO)).thenReturn(userAmountDTO);
,这是模拟userService.updateUserBalance
方法的适当方式。
然而,在@Test
**内部,userAmountDTO的内存地址可能会是@21835tj3之类的东西。在我的控制器中,userAmountDTO = userService.updateUserBalance(userId, userAmountDTO);
,传递给userService.updateUserBalance
方法的userAmountDTO的内存地址可能会不同,比如@21943jf3,这就是为什么它与单元测试中的模拟服务参数不匹配的原因。具有相同字段和值的对象并不意味着它们共享相同的引用,因此模拟的方法没有返回预期的值。
为了解决这个问题:
使用ArgumentMatchers.any()
Mockito来匹配UserAmountDTO
类的任何对象。因此,在单元测试中更改模拟方法为when(userService.updateUserBalance(eq(1), ArgumentMatchers.any(UserAmountDTO))).thenReturn(userAmountDTO);
这通常发生在POST请求中,因为我们传递了一个反序列化的JSON对象。还要感谢@JorgeCampos支持调试过程。
英文:
UPDATE ON UNIT TESTING POST REQUEST ON RESTCONTROLLER
I managed to resolve the mistake and in summary it is because I was passing in an object to the mocked service a different memory address. To put it simply, when(userService.updateUserBalance(1, userAmountDTO)).thenReturn(userAmountDTO);
, this is an appropriate way to mock the userService.updateUserBalance method.
However, inside the @Test
, the userAmountDTO memory address could be something like @21835tj3. In my controller, userAmountDTO = userService.updateUserBalance(userId, userAmountDTO);
, the memory address of userAmountDTO being passed into the userService.updateUserBalance
method could be different like @21943jf3 which was why it does not match the mocked service arguments in the unit test. Objects with the same fields and values doesn't mean they share the same reference, thus the mocked method did not return the expected value.
To fix this:
Use the ArgumentMatchers.any()
Mockito to match any object of the UserAmountDTO
class. So, change the mocked method in the unit test to when(userService.updateUserBalance(eq(1), ArgumentMatchers.any(UserAmountDTO))).thenReturn(userAmountDTO);
This usually happens with POST requests as we pass in a deserialized JSON object. Also thanks to @JorgeCampos for supporting with the debug process.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论