过去几个月我一直在Java项目(1.8和11)中工作。我们正在尝试将模型中的每个验证编码为自定义javax验证,我认为这是迄今为止我发现的“最佳”方法(一如既往地接受新建议)... 但是,问题在于,当该验证器需要从数据库和同一模型/实体的多个验证器中获取数据时。
实体
@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();
}
}
测试文件
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));
}
主要原因是用户可能还有其他验证器,而我不想将它们作为一个单元进行验证。
这是第一个问题...第二个问题与spring上下文有关(与往常一样)...上面的代码,即使使用Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
也不能正常工作,因为验证程序代码中的存储库为空。即使我运行它,也无法正确注入它,但仍按预期运行。我一直在使用以下注释@RunWith(SpringRunner.class)
,@DataJpaTest
和以下代码:
@Primary
@Bean
public Validator validator(){
return new LocalValidatorFactoryBean();
}
他们都没有按预期工作。我弄错了哪一块拼图?
谢谢!
答案 0 :(得分:3)
我认为您可以为此使用@TestConfiguration。例如,让您的班级以原始发布状态显示,然后将以下行添加到测试班级:
@TestConfiguration
static class DuplicatedUsernameValidatorTestContextConfiguration {
//for 'validator.validate(user);' usecase
@Bean
public Validator employeeService() {
return new LocalValidatorFactoryBean();
}
//for 'validator.isValid(user,null)' usecase
@Bean
public DuplicateEmployeeValidator employeeService() {
return new DuplicateEmployeeValidator();
}
}
@Autowired
private DuplicateEmployeeValidator dupValidator;
@Autowired
private Validator validator;
// private static Validator validator;
// @BeforeClass
// public static void setUpValidator() {
// ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
// validator = factory.getValidator();
// }
@MockBean
private UserRepository repository;
不需要添加@ DataJpaTest,UserRepository将自动注入到验证器bean中。
答案 1 :(得分:1)
我个人不是所有注释驱动的单元测试的忠实拥护者,建议将简单的构造函数添加到您的DuplicatedUsernameValidator
类中,以允许您在构造时设置UserRepository
。 / p>
public class DuplicatedUsernameValidator implements ConstraintValidator<DuplicatedUsername, User> {
@Autowired
private UserRepository userRepository;
// NEW!
public DuplicatedUsernameValidator(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public boolean isValid(User user, ConstraintValidatorContext context) {
...
}
}
然后在您的测试中,我将考虑执行以下操作:
private static DuplicatedUsernameValidator validator;
@MockBean
private UserRepository repository;
@BeforeClass
public static void setUpValidator() {
// We're not testing the factory here, I would NOT use it...
// Perhaps the factory should have its own test...
//ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = new DuplicatedUsernameValidator(repository);
}
public void givenDuplicatedSettings_whenValidate_thenReturnInvalid() {
//I appreciate the use of the given_when_then method names.
}
以下代码段中隐藏了以下注释:
Validation.buildDefaultValidatorFactory()
调用。一般来说,测试只应该测试被测类,我认为这实际上是在测试两件事。Validation.buildDefaultValidatorFactory()
方法的工作原理,那么我建议您对该类/方法进行单独的测试。答案 2 :(得分:1)
我能够使用Import
批注解决此问题。其他答案也可以,所以这是优先选择的问题
@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));
}
}