如何测试使用JPA存储库的特定javax自定义验证器?

时间:2019-10-29 22:42:38

标签: java spring unit-testing junit

过去几个月我一直在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();
    }

他们都没有按预期工作。我弄错了哪一块拼图?

谢谢!

3 个答案:

答案 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()方法的工作原理,那么我建议您对该类/方法进行单独的测试。
  • 我非常感谢使用GivenWhenThen单元测试方法名称。

答案 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));
    }
}