Issue
I've been working in a Java project (1.8 and 11) for the past few months. We are trying to code every validation we have in our models as a custom javax validation, which I think is the "best" method I've found so far (open to new suggestions as always)... The issue, though, is when this validator needs to fetch data from the database and multiple validators for the same model/entity.
Entity
@DuplicatedUsername
@Getter
@Setter
public class User {
private UUID id;
private String username;
public User(username) {
this.username = username;
}
}
Validator
public class DuplicatedUsernameValidator implements ConstraintValidator<DuplicatedUsername, User> {
@Autowired
private UserRepository userRepository;
@Override
public boolean isValid(User user, ConstraintValidatorContext context) {
if (user.getId() != null) {
return checkForDuplicatedUsername(user.getUsername(), user.getId());
} else {
return checkForDuplicatedUsername(user.getUsername());
}
}
private boolean checkForDuplicatedUsername(String username) {
List<User> users = userRepository.findByUsername(username);
return users.isEmpty();
}
private boolean checkForDuplicatedUsername(String username, UUID id) {
List<User> users = userRepository.findByUsernameAndIdNot(username, id);
return users.isEmpty();
}
}
Test file
private static Validator validator;
@BeforeClass
public static void setUpValidator() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@MockBean
private UserRepository repository;
public void givenDuplicatedSettings_whenValidate_thenReturnInvalid() {
String username = "duplicated";
User user = new User(username);
when(repository.findByUsername(username))
.thenReturn(new User(username));
// What I have
Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
assertEquals(1, constraintViolations.size());
// What I would like to have
// validator here must be DuplicatedUsernameValidator
assertFalse(validator.isValid(user));
}
The main reason for this is that User may have other validators, and I don't want to validate them as a single unit.
This is the first issue... The second is related to spring contexts (as always)... The above code, even with Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
doesn't work due to repository being null in the validator code. It's not being injected correctly even though when I run it, it's working as expected. I've been using the following annotations @RunWith(SpringRunner.class)
, @DataJpaTest
, and the following code:
@Primary
@Bean
public Validator validator(){
return new LocalValidatorFactoryBean();
}
None of them working as expected. Which piece of the puzzle am I getting wrong?
Thanks in advance!
Solution
I was able to fix this issue by using the Import
annotation. The other answers also work, so it's a matter of preference
@RunWith(SpringRunner.class)
@DataJpaTest
@Import(DuplicatedUsernameValidator.class)
public class DuplicatedUsernameValidatorTest {
@Aurowired
private DuplicatedUsernameValidator validator;
@MockBean
private UserRepository repository;
public void givenDuplicatedSettings_whenValidate_thenReturnInvalid() {
String username = "duplicated";
User user = new User(username);
when(repository.findByUsername(username))
.thenReturn(new User(username));
assertFalse(validator.isValid(user));
}
}
Answered By - Felipe Zigmundo