Issue
I have a method on a controller to get a list of all the punishment types for a chat room (Kick, Ban, Warn and Mute). On the first test when I mock the data it works as expected and the test passes.
However, on my second test I provided. I defined what should be returned as an Optional<Punishment>
with the attribute of punishmentName set as "mute". I am very confused why this is giving me null
. When I run the Spring application outside of testing, the route works fine. For some reason the mock never wants to return the value I specified but only null. Specifically, this is being caught in the test on the line .andExpect(jsonPath("$.punishmentName", Matchers.equalTo("mute")));
as the fields value is null giving the following error:
java.lang.AssertionError: No value at JSON path "$.punishmentName"
For clarity, I have also provided the controller methods and service methods.
Punishment Controller Test:
@WebMvcTest(PunishmentController.class)
@RunWith(SpringRunner.class)
public class PunishmentControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private PunishmentService punishmentService;
@MockBean
private PunishmentValidator punishmentValidator;
@Test
public void getAllPunishmentTypesReturnsAListOfPunishmentTypes() throws Exception {
List<Punishment> punishments = new ArrayList<>();
punishments.add(new Punishment("mute"));
punishments.add(new Punishment("kick"));
punishments.add(new Punishment("ban"));
punishments.add(new Punishment("warn"));
Mockito.when(punishmentService.getAllPunishmentTypes()).thenReturn(punishments);
mvc.perform(get("/api/punishments"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.hasSize(4)))
.andExpect(jsonPath("$[0].punishmentName", Matchers.equalTo("mute")))
.andExpect(jsonPath("$[1].punishmentName", Matchers.equalTo("kick")))
.andExpect(jsonPath("$[2].punishmentName", Matchers.equalTo("ban")))
.andExpect(jsonPath("$[3].punishmentName", Matchers.equalTo("warn")));
}
@Test
public void getPunishmentTypeReturnsMuteWhenMuteIsSpecified() throws Exception {
Optional<Punishment> mute = Optional.of(new Punishment("mute"));
Mockito.when(punishmentService.getPunishmentType("mute")).thenReturn(mute);
mvc.perform(get("/api/punishments/mute"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.punishmentName", Matchers.equalTo("mute")));
}
Controller methods:
/**
* GET request for all punishment types.
* @return List<Punishment> - When Punishments are found in the database they are returned in a List object.
* Otherwise, an empty list is returned if no records are found or an error occurs.
*/
@GetMapping
public List<Punishment> getAllPunishments() {
return punishmentService.getAllPunishmentTypes();
}
/**
* GET request for one punishment type.
* @param punishmentType String - The type of punishment.
* @return Optional<Punishment> - The rule that gets returned or an empty optional if no rule is found.
*/
@GetMapping(path = "{punishmentType}")
public Optional<Punishment> getPunishment(@PathVariable("punishmentType") String punishmentType) {
boolean isPunishmentTypeValid = punishmentValidator.validatePunishmentName(punishmentType);
if (isPunishmentTypeValid) {
return punishmentService.getPunishmentType(punishmentType);
} else {
return Optional.empty();
}
}
}
Service methods:
/**
* Gets all the punishment types
* @return List<Punishment> - The rules in the community
*/
public List<Punishment> getAllPunishmentTypes() {
return punishmentRepository.findAll();
}
/**
* Gets a specific punishment type.
* @param punishmentType String - The type of punishment.
* @return The punishment retrieved.
*/
public Optional<Punishment> getPunishmentType(String punishmentType) {
return punishmentRepository.findById(punishmentType);
}
Solution
I believe it is because you forget to mock the method PunishmentValidator#validatePunishmentName("mute")
to return true
such that the method that you stub on PunishmentService
is never invoked because by default if you do not stub a method , it will return false (see this).
Also it is a known behaviour that @MockBean
is configured as lenient stubbing which will not reported error (i.e. throw UnnecessaryStubbingException
) if you stub a method but it actually does not get executed.
So change the following should fix your problem :
@Test
public void getPunishmentTypeReturnsMuteWhenMuteIsSpecified() throws Exception {
Optional<Punishment> mute = Optional.of(new Punishment("mute"));
Mockito.when(punishmentService.getPunishmentType("mute")).thenReturn(mute);
Mockito.when(punishmentValidator.validatePunishmentName("mute")).thenReturn(true);
mvc.perform(get("/api/punishments/mute"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.punishmentName", Matchers.equalTo("mute")));
}
Answered By - Ken Chan
Answer Checked By - Gilberto Lyons (JavaFixing Admin)