How to mock jakarta.validation.ConstraintValidator or its dependencies?

44 Views Asked by At

I am trying to create a test to a very simple controller that uses a validator to validate one of its parameters. Here is the controller:

@RestController
@Validated
public class VeryNiceController {

    @GetMapping("/very/nice")
    public ResponseEntity<String> nice(
        @DateRangeConfig(required = DateRangeConfig.Required.NONE) @ValidNotificationDate DateRange dateRange
    ) {
        return ResponseEntity.ok(dateRange.toString());
    }
}

As you might deduce this is a very simple controller that serves only to illustrate a point. In my test, I want to override the clock that is used by the validator. The validator gets the clock from the context of type ConstraintValidatorContext passed to the isValid method as illustrated bellow:

public class ValidNotificationDateValidator implements ConstraintValidator<ValidNotificationDate, DateRange> {

    ...

    @Override
    public boolean isValid(DateRange value, ConstraintValidatorContext context) {
        final Instant lowestPossibleLookup = Instant.from(Instant.now(context.getClockProvider().getClock()).minus(notificationPropertiesConfig.getExpiryMax()));

...

Here's the test code:

@WebMvcTest(controllers = VeryNiceController.class)
@Import({WebSecurityConfig.class, RequestIdService.class, ObjectMapperConfig.class})
@TestPropertySource(locations = "classpath:mock-user.properties")
public class VeryNiceControllerTest {

    static final Clock TEST_CLOCK = Clock.fixed(Instant.ofEpochSecond(1627983134), ZoneId.of("UTC"));
    static final Instant NOW = TEST_CLOCK.instant();

    @TestConfiguration
    static class VeryNiceConfiguration {

        @Bean
        public ClockProvider clockProvider() {
            return () -> TEST_CLOCK;
        }

//        @Bean
//        ConstraintValidatorContext constraintValidatorContext() {
//            return mock(ConstraintValidatorContext.class);
//        }


    }

    @Autowired
    MockMvc mockMvc;

//    @SpyBean
//    ValidNotificationDateValidator validNotificationDateValidator;

//    @MockBean
    @SpyBean
    NotificationPropertiesConfig propertiesConfig;

//    @SpyBean
//.   @MockBean
//    ConstraintValidatorContextImpl constraintValidatorContext;

//    @MockBean
//    ClockProvider clockProvider;


    @Test
    @WithMockUser
    void testVeryNiceEndpoint() throws Exception {


//        when(validNotificationDateValidator.isValid(any(), any())).thenReturn(true);
//            doReturn(false).when(validNotificationDateValidator).isValid(any(), any());

//        when(clockProvider.getClock()).thenReturn(TEST_CLOCK);
        doReturn(Period.ZERO).when(propertiesConfig).getExpiryMax();
//        doReturn(clockProvider).when(constraintValidatorContext).getClockProvider();

        this.mockMvc.perform(
            get("/very/nice")
        )
        .andDo(print())
        .andExpect(status().isOk());
    }
}

As you can see I tried several things. Creating mock beans, spying on them, for the clock bean, for the context bean and also for the validator itself but although these mock beans are created and are in the application context (if I inspect it in the test code) when the test execution gets to the controller, the actual beans are used and not the mocks. I am out of ideas and would appreciate if anyone could provide some other pathways for me to follow.

Thank you

P.S.: This project runs with springboot 3.0.5 and java 17.

0

There are 0 best solutions below