Hi,

I've got simple RestController with two endpoints, one POST on / for a single entity and one POST on /bulk for a list of entities. Both are annotated with @Valid, and the RestController is annotated @Validated.

All works fine and throws errors when missing @NotBlank entity fields, but when writing unit tests with WebTestClient I found out that the @Valid on the List of entities is ignored. Testing the single entity POST properly throws a 400 though.

Here's the controller:

@Validated
@RestController
@RequestMapping("/entities")
public class ApiController {

    @PostMapping
    public ResponseEntity save(@Valid @RequestBody Entity entity) {
        //deal with valid entity
    }

    @PostMapping("/bulk")
    @NotEmpty(message = "List is not allowed to be empty")
    public ResponseEntity saveAll(@RequestBody @Valid List<Entity> entities) {
        //deal with valid entities
    }
}

This is the JUnit test method:

@Test
void testSaveAllShouldFailWhenRequiredFieldsAreMissing() {
    WebTestClient.bindToController(apiController).build()
        .post()
        .uri("/entities/bulk")
        .bodyValue(List.of(
                Entity.builder().requiredField("dummy").build(),
                Entity.builder().build()))
        .exchange()
        .expectStatus().isBadRequest();
}

The requiredField in the entity is annotated with @NotBlank.

I would expect this test to be succesfull (eg. the WebTestClient should receive a 400) but it doesn't. A similar test for the rest endpoint with a single entity works as expected.

Comment From: sbrannen

I've edited your comment to improve the formatting. You might want to check out this Mastering Markdown guide for future reference.

Comment From: sbrannen

How is the apiController used in your test created?

Do you instantiate it yourself?

Is it created by the ApplicationContext?

Comment From: sbrannen

Also, what happens if you rewrite your /bulk handler method as follows (i.e., with List<@Valid Entity>)?

    @PostMapping("/bulk")
    @NotEmpty(message = "List is not allowed to be empty")
    public ResponseEntity saveAll(@RequestBody @Valid List<@Valid Entity> entities) {
        //deal with valid entities
    }

Comment From: gvermoen

Hi, thanks for your quick reply! My apiController is created by Mockito like this:

@ExtendWith(MockitoExtension.class)
class ApiControllerTest {

    @Mock
    private EntityService entityService;

    @InjectMocks
    private ApiController apiController;

I tried with your suggestion, it didn't help. Also I found out that the @NotEmpty annotation doesnt work as I expected, so I removed that one but that also doesn't help.

Comment From: sbrannen

Mockito will not apply a Spring proxy to honor the @Validated or @Valid annotations.

The needed proxy creation is performed by MethodValidationPostProcessor which you would need to register as a bean in the ApplicationContext. You would then need to acquire your controller as a bean from the same ApplicationContext. One way to do that would be to use the Spring TestContext Framework to load your test ApplicationContext and then have your proxied controller @Autowired into your test. For details, consult the Testing chapter of the Spring Framework reference manual.

For any further questions on this topic, please use Stack Overflow.

Comment From: gvermoen

OK thanks. I guess the fact that @Valid on a single entity actually works has put me on the wrong foot. Thanks for your directions.