Как проверить конкретный пользовательский валидатор javax, который использует репозитории JPA? - PullRequest
1 голос
/ 30 октября 2019

Я работаю в проекте Java (1.8 и 11) последние несколько месяцев. Мы пытаемся закодировать каждую валидацию, которую мы имеем в наших моделях, в качестве пользовательской валидации javax, которая, на мой взгляд, является «лучшим» методом, который я нашел на данный момент (как всегда, открытым для новых предложений) ... Проблема, однако, заключается в том,когда этому валидатору нужно извлечь данные из базы данных и нескольких валидаторов для одной и той же модели / объекта.

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();
  }
}

Тестовый файл

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

Основная причина этого заключается в том, что у пользователя могут быть другие валидаторы, и я не хочу проверять их как единое целое.

Это первая проблема ... Втораясвязан с пружинным контекстом (как всегда) ... Приведенный выше код, даже с Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);, не работает из-за того, что хранилище является нулевым в коде валидатора. Он не вводится правильно, хотя, когда я его запускаю, он работает как положено. Я использовал следующие аннотации @RunWith(SpringRunner.class), @DataJpaTest и следующий код:

    @Primary
    @Bean
    public Validator validator(){
        return new LocalValidatorFactoryBean();
    }

Ни один из них не работал должным образом. В какой части головоломки я ошибаюсь?

Заранее спасибо!

Ответы [ 3 ]

3 голосов
/ 03 ноября 2019

Я думаю, что вы можете использовать @ 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 голос
/ 06 ноября 2019

Мне удалось решить эту проблему с помощью аннотации 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));
    }
}
1 голос
/ 06 ноября 2019

Я лично не большой поклонник всех модульных тестов, управляемых аннотациями, и предложил бы просто добавить к вам класс DuplicatedUsernameValidator, который позволил бы вам установить UserRepository во время построения.

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 имен методов модульного теста.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...