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.