Issue
I'm struggling with a simple spring boot rest controller test which always return empty body response.
Here is my test code looks like:
@WebMvcTest(AdminRestController.class)
@AutoConfigureMockMvc(addFilters = false)
public class PatientsUnitTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private PatientsService patientsService;
@MockBean
private TherapistsService therapistsService;
@MockBean
private TherapySchedulesService therapySchedulesService;
@Test
public void canAddPatient() throws Exception {
PatientsSaveRequestDto patientsSaveRequestDto = new PatientsSaveRequestDto();
patientsSaveRequestDto.setName("Sofia");
patientsSaveRequestDto.setPhone("01012345678");
Patients patient = patientsSaveRequestDto.toEntity();
when(patientsService.createPatient(patientsSaveRequestDto)).thenReturn(patient);
final ResultActions actions = mvc.perform(post("/admin/patient")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.characterEncoding(StandardCharsets.UTF_8.name())
.content(objectMapper.writeValueAsString(patientsSaveRequestDto)))
.andDo(print());
actions
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("name", is(patient.getName())))
.andDo(print());
}
My Controller:
@RestController
@RequiredArgsConstructor
public class AdminRestController {
private final PatientsService patientsService;
private final TherapistsService therapistsService;
private final TherapySchedulesService therapySchedulesService;
@PostMapping("/admin/patient")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Create a patient")
public Patients cratePatient(
@RequestBody @Valid PatientsSaveRequestDto patientsSaveRequestDto
) {
return patientsService.createPatient(patientsSaveRequestDto);
}
// PatientsService
@Transactional
public Patients createPatient(PatientsSaveRequestDto patientsSaveRequestDto){
return patientsRepository.save(patientsSaveRequestDto.toEntity());
}
And this is the result of print():
MockHttpServletRequest:
HTTP Method = POST
Request URI = /admin/patient
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"53"]
Body = {"name":"sofia","phone":"01012345678","tel":null}
Session Attrs = {}
Handler:
Type = com.ussoft.dosu.web.controller.admin.AdminRestController
Method = com.ussoft.dosu.web.controller.admin.AdminRestController#cratePatient(PatientsSaveRequestDto)
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 = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
As you can see the request is sent correctly but the response values are all null.
When i test the same controller using @SpringBootTest with Rest Assured it works fine.
I'm using Spring boot 2.3.1, Junit5
Edit - added PatientsSaveRequestDto
@Getter
@Setter
@NoArgsConstructor
public class PatientsSaveRequestDto {
@NotBlank(message = "이름은 필수 입력사항입니다.")
private String name;
private String phone;
private String tel;
public Patients toEntity(){
return Patients.builder()
.name(name)
.phone(phone)
.tel(tel)
.build();
}
}
Solution
You need to provide equals method for PatientsSaveRequestDto
.
When you execute a method on a mock, Mockito needs to check if any behaviour was specified for the arguments for which the method was called.
- If the arguments match, the recorded result is returned,
- If the arguments don't match, default value of the method return type is returned (null for all Objects, zero for numerics, false for bools)
You recorded the behaviour with the following call:
when(patientsService.createPatient(patientsSaveRequestDto)).thenReturn(patient);
This means that the actual argument to createPatient
will be compared to patientsSaveRequestDto
with equals
.
Note that this behaviour can be changed by the use of ArgumentMatchers.
The patientsSaveRequestDto
from your test and the actual argument to createPatient
are not equal because:
- you didn't define the equals method
- they are different instances
- thus, the inherited Object.equals returns false
You have 2 different instances because you created a @WebMvcTest.
The patientsSaveRequestDto
you send to the controller is first serialized to String and then deserialized, and this is how a second instance got created.
Answered By - Lesiak
Answer Checked By - Pedro (JavaFixing Volunteer)