Ограничение @ZipCode написать сложнее, чем @Email. ZIP-код имеет определенный формат (например, в США он состоит из пяти цифр), который не составляет труда проверить с помощью регулярного выражения. Но чтобы гарантировать, что ZIP-код не только синтаксически верен, но и валиден, необходимо прибегнуть к внешней службе, которая будет проверять, существует ли конкретный ZIP-код в базе данных. Именно поэтому ограничивающая аннотация ZipCode в листинге 3.25 требует класса реализации (ZipCodeValidator).

Листинг 3.25. Ограничивающая аннотация @ZipCode

@Constraint(validatedBy = ZipCodeValidator.class)

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})

@Retention(RUNTIME)

public @interface ZipCode {

··String message() default "{org.agoncal.book.javaee7.chapter03.ZipCode.message}";

··Class[] groups() default {};

··Class[] payload() default {};

··@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})

··@Retention(RUNTIME)

··@interface List {

····ZipCode[] value();

··}

}

В листинге 3.26 показан класс реализации ограничивающей аннотации: ZipCodeValidator реализует интерфейс javax.validation.ConstraintValidator с обобщенным типом String. Метод isValid реализует алгоритм валидации, в рамках которого выполняется сопоставление с шаблоном регулярного выражения и происходит вызов внешнего сервиса: ZipCodeChecker. Код ZipCodeChecker здесь не показан, так как в данном случае он неважен. Но необходимо отметить, что он внедряется (@Inject) с квалификатором CDI (@USA, показан в листинге 3.27). Итак, здесь мы наблюдаем взаимодействие спецификаций CDI и Bean Validation.

Листинг 3.26. Реализация ограничения ZipCodeValidator

public class ZipCodeValidator implements ConstraintValidator {

··@Inject @USA

··private ZipCodeChecker checker;

··private Pattern zipPattern = Pattern.compile("\\d{5}(-\\d{5})?");

··public void initialize(ZipCode zipCode) {

··}

··public boolean isValid(String value, ConstraintValidatorContext context) {

····if (value == null)

······return true;

····Matcher m = zipPattern.matcher(value);

····if (!m.matches())

····return false;

····return checker.isZipCodeValid(value);

··}

}

Листинг 3.27. Квалификатор USACDI

@Qualifier

@Retention(RUNTIME)

@Target({FIELD, TYPE, METHOD})

public @interface USA {

}

Примечание

В следующих главах будет показано, как интегрировать валидацию компонентов с другими спецификациями, в частности JPA (можно добавлять ограничения к вашим сущностям) или JSF (можно ограничивать базовые компоненты).

<p>Написание интеграционных тестов CustomerIT и AddressIT</p>

Как мы теперь можем протестировать ограничения, налагаемые на наши компоненты? Мы ведь не можем писать модульные тесты для @Email, так как это аннотация, агрегирующая ограничения, равно как и для @ZipCode, которое может работать только для внедрения (а это контейнерная служба). Проще всего будет написать интеграционный тест, то есть использовать фабрику ValidatorFactory для получения Validator, а потом валидировать наши компоненты.

В листинге 3.28 показан класс CustomerIT, выполняющий интеграционное тестирование компонента Customer. Метод инициализирует Validator (с помощью ValidatorFactory), а метод close() высвобождает фабрику. Класс далее содержит два теста: в одном создается валидный объект Customer, а в другом создается объект с недопустимым адресом электронной почты и проверяется, окончится ли валидация ошибкой.

Листинг 3.28. Интеграционный тест CustomerIT

public class CustomerIT {

··private static ValidatorFactory vf;

··private static Validator validator;

Перейти на страницу:

Похожие книги