Skip to content

Commit d48de85

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

File tree

9 files changed

+385
-1
lines changed

9 files changed

+385
-1
lines changed

documentation/pom.xml

+6-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<!-- Skip artifact deployment -->
3535
<maven.deploy.skip>true</maven.deploy.skip>
3636
<gpg.skip>true</gpg.skip>
37-
<surefire.jvm.args.additional>-Duser.language=en -Duser.country=US</surefire.jvm.args.additional>
37+
<surefire.jvm.args.additional>--add-opens java.base/java.lang=ALL-UNNAMED -Duser.language=en -Duser.country=US</surefire.jvm.args.additional>
3838

3939
<forbiddenapis-junit.path>forbidden-allow-junit.txt</forbiddenapis-junit.path>
4040
<hibernate-validator-parent.path>..</hibernate-validator-parent.path>
@@ -97,6 +97,11 @@
9797
<artifactId>junit</artifactId>
9898
<scope>test</scope>
9999
</dependency>
100+
<dependency>
101+
<groupId>org.easymock</groupId>
102+
<artifactId>easymock</artifactId>
103+
<scope>test</scope>
104+
</dependency>
100105
<!-- Only use assertj when strictly necessary in the documentation tests -->
101106
<dependency>
102107
<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,56 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
6+
7+
import static org.easymock.EasyMock.eq;
8+
import static org.easymock.EasyMock.expect;
9+
import static org.easymock.EasyMock.isA;
10+
import static org.easymock.EasyMock.mock;
11+
import static org.easymock.EasyMock.replay;
12+
import static org.easymock.EasyMock.verify;
13+
import static org.junit.Assert.assertEquals;
14+
15+
import java.util.Map;
16+
import java.util.Set;
17+
18+
import jakarta.validation.ConstraintValidatorContext;
19+
import jakarta.validation.ConstraintViolation;
20+
import jakarta.validation.Validator;
21+
import jakarta.validation.ValidatorFactory;
22+
23+
import org.hibernate.validator.testutil.PreconfiguredValidatorsValidatorFactory;
24+
25+
import org.junit.Test;
26+
27+
@SuppressWarnings("unused")
28+
//tag::field[]
29+
public class CustomValidatorWithDependencyTest {
30+
31+
@Test
32+
public void mockCustomValidatorWithDependency() {
33+
ZipCodeValidator zipCodeValidator = mock( ZipCodeValidator.class );
34+
35+
expect( zipCodeValidator.isValid( eq( "1234" ), isA( ConstraintValidatorContext.class ) ) )
36+
.andStubReturn( true );
37+
zipCodeValidator.initialize( isA( ZipCode.class ) );
38+
39+
replay( zipCodeValidator );
40+
41+
ValidatorFactory validatorFactory = PreconfiguredValidatorsValidatorFactory.builder()
42+
.defaultValidators( Map.of( ZipCodeValidator.class, zipCodeValidator ) )
43+
.build();
44+
45+
Validator validator = validatorFactory.getValidator();
46+
47+
Person person = new Person( "1234" );
48+
49+
Set<ConstraintViolation<Person>> constraintViolations = validator.validate( person );
50+
51+
assertEquals( 0, constraintViolations.size() );
52+
53+
verify( zipCodeValidator );
54+
}
55+
}
56+
//end::field[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
//tag::include[]
6+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
7+
8+
public class Person {
9+
10+
@ZipCode
11+
private String zipCode;
12+
13+
public Person(String zipCode) {
14+
this.zipCode = zipCode;
15+
}
16+
}
17+
//end::include[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
//tag::include[]
6+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
7+
8+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
9+
import static java.lang.annotation.ElementType.FIELD;
10+
import static java.lang.annotation.ElementType.METHOD;
11+
import static java.lang.annotation.ElementType.TYPE_USE;
12+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
13+
14+
import java.lang.annotation.Documented;
15+
import java.lang.annotation.Retention;
16+
import java.lang.annotation.Target;
17+
18+
import jakarta.validation.Constraint;
19+
import jakarta.validation.Payload;
20+
21+
//tag::include[]
22+
@Target({ METHOD, FIELD, ANNOTATION_TYPE, TYPE_USE })
23+
@Retention(RUNTIME)
24+
@Constraint(validatedBy = ZipCodeValidator.class)
25+
@Documented
26+
public @interface ZipCode {
27+
28+
String message() default "{org.hibernate.validator.referenceguide.chapter06." +
29+
"customvalidatorwithdependency.ZipCode.message}";
30+
31+
Class<?>[] groups() default { };
32+
33+
Class<? extends Payload>[] payload() default { };
34+
}
35+
//end::include[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
6+
7+
public interface ZipCodeRepository {
8+
boolean isExist(String zipCode);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
//tag::include[]
6+
package org.hibernate.validator.referenceguide.chapter06.customvalidatorwithdependency;
7+
8+
//end::include[]
9+
10+
import jakarta.inject.Inject;
11+
import jakarta.validation.ConstraintValidator;
12+
import jakarta.validation.ConstraintValidatorContext;
13+
14+
//tag::include[]
15+
public class ZipCodeValidator implements ConstraintValidator<ZipCode, String> {
16+
17+
@Inject
18+
public ZipCodeRepository zipCodeRepository;
19+
20+
@Override
21+
public boolean isValid(String zipCode, ConstraintValidatorContext constraintContext) {
22+
if ( zipCode == null ) {
23+
return true;
24+
}
25+
26+
return zipCodeRepository.isExist( zipCode );
27+
}
28+
}
29+
//end::include[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.validator.testutil;
6+
7+
import java.util.HashMap;
8+
import java.util.Map;
9+
10+
import jakarta.validation.ConstraintValidator;
11+
import jakarta.validation.ConstraintValidatorFactory;
12+
13+
public class PreconfiguredConstraintValidatorFactory implements ConstraintValidatorFactory {
14+
15+
private final Map<Class<? extends ConstraintValidator>, ConstraintValidator<?, ?>> defaultValidators;
16+
private final ConstraintValidatorFactory delegated;
17+
18+
private PreconfiguredConstraintValidatorFactory(Builder builder) {
19+
this.defaultValidators = builder.defaultValidators;
20+
this.delegated = builder.delegated;
21+
}
22+
23+
public static Builder builder() {
24+
return new Builder();
25+
}
26+
27+
@SuppressWarnings("unchecked")
28+
@Override
29+
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
30+
if ( defaultValidators.containsKey( key ) ) {
31+
return (T) defaultValidators.get( key );
32+
}
33+
34+
return delegated.getInstance( key );
35+
}
36+
37+
@Override
38+
public void releaseInstance(ConstraintValidator<?, ?> instance) {
39+
delegated.releaseInstance( instance );
40+
}
41+
42+
public static class Builder {
43+
44+
private ConstraintValidatorFactory delegated;
45+
private final Map<Class<? extends ConstraintValidator>, ConstraintValidator<?, ?>> defaultValidators = new HashMap<>();
46+
47+
private Builder() {
48+
}
49+
50+
public Builder defaultValidators(
51+
Map<Class<? extends ConstraintValidator>, ConstraintValidator<?, ?>> validators) {
52+
this.defaultValidators.putAll( validators );
53+
return this;
54+
}
55+
56+
public Builder delegated(
57+
ConstraintValidatorFactory delegated) {
58+
this.delegated = delegated;
59+
return this;
60+
}
61+
62+
public PreconfiguredConstraintValidatorFactory build() {
63+
return new PreconfiguredConstraintValidatorFactory( this );
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)