I’m building a Spring Boot application and am working on testing my controller with @WebMvcTest.
I have a controller method that takes in @Validated @RequestBody UpdateFoodReqBody (record) which has a field @Validated @NotNull UpdateProductReqBody (record) in it that I also want to validate.
The test works when the product object is null but not when the product is present but its fields are invalid.
@PostMapping("/update-food/{foodId}")
public GetFoodResponse updateFood(
@PathVariable @NotNull Long foodId,
@Validated @RequestBody UpdateFoodReqBody updatedFood) {
return foodService.updateFood(foodId, updatedFood);
}
public record UpdateFoodReqBody(
String dietaryRestrictions,
@Validated @NotNull UpdateProductReqBody product
) {
}
@Validated
public record UpdateProductReqBody(
@NotBlank String name,
String description,
@NotNull @Positive BigDecimal price,
@NotNull Date pickupTime
) {
}
Successful test:
@Test
public void testUpdateFood_invalidInput_forStackOverflow() throws Exception {
mockMvc.perform(MockMvcRequestBuilders
.post(controllerPath + "/update-food/1")
.accept(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(
new UpdateFoodReqBody("Restrictions", null)))
.contentType("application/json"))
.andExpect(result -> assertTrue(result.getResolvedException() instanceof MethodArgumentNotValidException))
.andExpect(status().isBadRequest());
}
Test succesful!
Failing test (… Resolved Exceptin: Type = null …):
@Test
public void testUpdateFood_invalidInput_forStackOverflow() throws Exception {
mockMvc.perform(MockMvcRequestBuilders
.post(controllerPath + "/update-food/1")
.accept(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(
new UpdateFoodReqBody(
"Restrictions",
new UpdateProductReqBody(null, "Description", null, new Date()))
))
.contentType("application/json"))
.andExpect(result -> assertTrue(result.getResolvedException() instanceof MethodArgumentNotValidException))
.andExpect(status().isBadRequest());
}
Causes:
Resolved Exception:
Type = null
At first I had @Validated annotation only in the controller class. Had the problem so also added @Validated to the records themselves, no difference.
Can someone please advise. Thank you!
Okay, I solved it myself. I’ll say what I did but someone smarter might chip in with more explanation because I have no idea WHY it worked.
Solution:
In the UpdateProductReqBody record I replaced
import org.springframework.validation.annotation.Validated
public record UpdateFoodReqBody(
String dietaryRestrictions,
@Validated @NotNull UpdateProductReqBody product
){}
with
import jakarta.validation.Valid
public record UpdateFoodReqBody(
String dietaryRestrictions,
@Valid @NotNull UpdateProductReqBody product
){}
It worked but I don’t know why. Isn’t @Validated just a Spring implementation of JSR-303’s jakarta.validation.Valid?
take a look at stackoverflow.com/questions/36173332/…