Skip to content

Commit a9c00b3

Browse files
committed
HV-1921 Create a tester library for constraint validators with dependency injection
1 parent feb157a commit a9c00b3

File tree

9 files changed

+374
-1
lines changed

9 files changed

+374
-1
lines changed

documentation/pom.xml

+11-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
<!-- Skip artifact deployment -->
4040
<maven.deploy.skip>true</maven.deploy.skip>
4141
<gpg.skip>true</gpg.skip>
42-
<surefire.jvm.args.additional>-Duser.language=en -Duser.country=US</surefire.jvm.args.additional>
42+
<surefire.jvm.args.additional>--add-opens java.base/java.lang=ALL-UNNAMED -Duser.language=en -Duser.country=US</surefire.jvm.args.additional>
4343

4444
<forbiddenapis-junit.path>forbidden-allow-junit.txt</forbiddenapis-junit.path>
4545
<hibernate-validator-parent.path>..</hibernate-validator-parent.path>
@@ -61,6 +61,11 @@
6161
<artifactId>hibernate-validator-cdi</artifactId>
6262
<scope>test</scope>
6363
</dependency>
64+
<dependency>
65+
<groupId>${project.groupId}</groupId>
66+
<artifactId>hibernate-validator-test-utils</artifactId>
67+
<scope>test</scope>
68+
</dependency>
6469
<dependency>
6570
<groupId>jakarta.enterprise</groupId>
6671
<artifactId>jakarta.enterprise.cdi-api</artifactId>
@@ -97,6 +102,11 @@
97102
<artifactId>junit</artifactId>
98103
<scope>test</scope>
99104
</dependency>
105+
<dependency>
106+
<groupId>org.easymock</groupId>
107+
<artifactId>easymock</artifactId>
108+
<scope>test</scope>
109+
</dependency>
100110
<!-- Only use assertj when strictly necessary in the documentation tests -->
101111
<dependency>
102112
<groupId>org.assertj</groupId>

documentation/src/main/asciidoc/ch06.asciidoc

+55
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,61 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/CarTest.ja
323323
----
324324
====
325325

326+
[[validator-dependency-testing]]
327+
==== Testing constraint validator with dependencies
328+
329+
Some DI frameworks (e.g. Spring) are capable of injecting dependencies into constraint validator instance:
330+
331+
[[example-person-with-checkcase]]
332+
.Hibernate Validator test utilities Maven dependency
333+
====
334+
[source, XML]
335+
[subs="verbatim,attributes"]
336+
----
337+
<dependency>
338+
<groupId>org.hibernate.validator</groupId>
339+
<artifactId>hibernate-validator-test-utils</artifactId>
340+
<version>{hvVersion}</version>
341+
<scope>test</scope>
342+
</dependency>
343+
----
344+
====
345+
346+
.Defining the `@ZipCode` constraint annotation
347+
====
348+
[source, JAVA, indent=0]
349+
----
350+
include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/customvalidatorwithdependency/ZipCode.java[tags=include]
351+
----
352+
====
353+
354+
.Applying the `@ZipCode` constraint
355+
====
356+
[source, JAVA, indent=0]
357+
----
358+
include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/customvalidatorwithdependency/Person.java[tags=include]
359+
----
360+
====
361+
362+
.Using injected dependency in a constraint validator
363+
====
364+
[source, JAVA, indent=0]
365+
----
366+
include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/customvalidatorwithdependency/ZipCodeValidator.java[tags=include]
367+
----
368+
====
369+
370+
Finally, <<example-using-validator-dependency>> demonstrates how validating a `Person` instance which calls custom mocked validator.
371+
372+
[[example-using-validator-dependency]]
373+
.Validating objects with the `@ZipCode` constraint
374+
====
375+
[source, JAVA, indent=0]
376+
----
377+
include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/customvalidatorwithdependency/CustomValidatorWithDependencyTest.java[tags=field]
378+
----
379+
====
380+
326381
[[section-class-level-constraints]]
327382
=== Class-level constraints
328383

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
2+
3+
import static org.easymock.EasyMock.eq;
4+
import static org.easymock.EasyMock.expect;
5+
import static org.easymock.EasyMock.isA;
6+
import static org.easymock.EasyMock.mock;
7+
import static org.easymock.EasyMock.replay;
8+
import static org.easymock.EasyMock.verify;
9+
import static org.junit.Assert.assertEquals;
10+
11+
import java.util.Map;
12+
import java.util.Set;
13+
14+
import jakarta.validation.ConstraintValidatorContext;
15+
import jakarta.validation.ConstraintViolation;
16+
import jakarta.validation.Validator;
17+
import jakarta.validation.ValidatorFactory;
18+
import org.hibernate.validator.testutil.PreconfiguredValidatorsValidatorFactory;
19+
import org.junit.Test;
20+
21+
@SuppressWarnings("unused")
22+
//tag::field[]
23+
public class CustomValidatorWithDependencyTest {
24+
25+
@Test
26+
public void mockCustomValidatorWithDependency() {
27+
ZipCodeValidator zipCodeValidator = mock( ZipCodeValidator.class );
28+
29+
expect( zipCodeValidator.isValid( eq( "1234" ), isA( ConstraintValidatorContext.class ) ) )
30+
.andStubReturn( true );
31+
zipCodeValidator.initialize( isA( ZipCode.class ) );
32+
33+
replay( zipCodeValidator );
34+
35+
ValidatorFactory validatorFactory = PreconfiguredValidatorsValidatorFactory.builder()
36+
.defaultValidators( Map.of( ZipCodeValidator.class, zipCodeValidator ) )
37+
.build();
38+
39+
Validator validator = validatorFactory.getValidator();
40+
41+
Person person = new Person( "1234" );
42+
43+
Set<ConstraintViolation<Person>> constraintViolations = validator.validate( person );
44+
45+
assertEquals( 0, constraintViolations.size() );
46+
47+
verify( zipCodeValidator );
48+
}
49+
}
50+
//end::field[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//tag::include[]
2+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
3+
4+
public class Person {
5+
6+
@ZipCode
7+
private String zipCode;
8+
9+
public Person(String zipCode) {
10+
this.zipCode = zipCode;
11+
}
12+
}
13+
//end::include[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//tag::include[]
2+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
3+
4+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
5+
import static java.lang.annotation.ElementType.FIELD;
6+
import static java.lang.annotation.ElementType.METHOD;
7+
import static java.lang.annotation.ElementType.TYPE_USE;
8+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
9+
10+
//end::include[]
11+
12+
import java.lang.annotation.Documented;
13+
import java.lang.annotation.Retention;
14+
import java.lang.annotation.Target;
15+
16+
import jakarta.validation.Constraint;
17+
import jakarta.validation.Payload;
18+
19+
//tag::include[]
20+
@Target({METHOD, FIELD, ANNOTATION_TYPE, TYPE_USE})
21+
@Retention(RUNTIME)
22+
@Constraint(validatedBy = ZipCodeValidator.class)
23+
@Documented
24+
public @interface ZipCode {
25+
26+
String message() default "{org.hibernate.validator.referenceguide.chapter06." +
27+
"customvalidatorwithdependency.ZipCode.message}";
28+
29+
Class<?>[] groups() default {};
30+
31+
Class<? extends Payload>[] payload() default {};
32+
}
33+
//end::include[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
2+
3+
public interface ZipCodeRepository {
4+
boolean isExist(String zipCode);
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//tag::include[]
2+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
3+
4+
//end::include[]
5+
6+
import jakarta.inject.Inject;
7+
import jakarta.validation.ConstraintValidator;
8+
import jakarta.validation.ConstraintValidatorContext;
9+
10+
//tag::include[]
11+
public class ZipCodeValidator implements ConstraintValidator<ZipCode, String> {
12+
13+
@Inject
14+
public ZipCodeRepository zipCodeRepository;
15+
16+
@Override
17+
public boolean isValid(String zipCode, ConstraintValidatorContext constraintContext) {
18+
if ( zipCode == null ) {
19+
return true;
20+
}
21+
22+
return zipCodeRepository.isExist( zipCode );
23+
}
24+
}
25+
//end::include[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.testutil;
8+
9+
import jakarta.validation.ConstraintValidator;
10+
import jakarta.validation.ConstraintValidatorFactory;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
public class PreconfiguredConstraintValidatorFactory implements ConstraintValidatorFactory {
16+
17+
private final Map<Class<? extends ConstraintValidator>, ConstraintValidator<?, ?>> defaultValidators;
18+
private final ConstraintValidatorFactory delegated;
19+
20+
private PreconfiguredConstraintValidatorFactory(Builder builder) {
21+
this.defaultValidators = builder.defaultValidators;
22+
this.delegated = builder.delegated;
23+
}
24+
25+
public static Builder builder() {
26+
return new Builder();
27+
}
28+
29+
@SuppressWarnings("unchecked")
30+
@Override
31+
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
32+
if ( defaultValidators.containsKey( key ) ) {
33+
return (T) defaultValidators.get( key );
34+
}
35+
36+
return delegated.getInstance( key );
37+
}
38+
39+
@Override
40+
public void releaseInstance(ConstraintValidator<?, ?> instance) {
41+
delegated.releaseInstance( instance );
42+
}
43+
44+
public static class Builder {
45+
46+
private ConstraintValidatorFactory delegated;
47+
private final Map<Class<? extends ConstraintValidator>, ConstraintValidator<?, ?>> defaultValidators = new HashMap<>();
48+
49+
private Builder() {
50+
}
51+
52+
public Builder defaultValidators(
53+
Map<Class<? extends ConstraintValidator>, ConstraintValidator<?, ?>> validators) {
54+
this.defaultValidators.putAll( validators );
55+
return this;
56+
}
57+
58+
public Builder delegated(
59+
ConstraintValidatorFactory delegated) {
60+
this.delegated = delegated;
61+
return this;
62+
}
63+
64+
public PreconfiguredConstraintValidatorFactory build() {
65+
return new PreconfiguredConstraintValidatorFactory( this );
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)