I have an "AllowedValuesValidator.java" class:
public class AllowedValuesValidator implements ConstraintValidator<AllowedValues, String> { String[] values; String defaultValue; @Override public void initialize(AllowedValues constraintAnnotation) { values = constraintAnnotation.allowedValues(); defaultValue = constraintAnnotation.defaultValue(); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (!StringUtils.isEmpty(defaultValue) && StringUtils.isEmpty(value)) { value = defaultValue; } if (!StringUtils.isEmpty(value) && !Arrays.asList(values).contains(value)) { return false; } return true; } }
And the corresponding interface class:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = AllowedValuesValidator.class) public @interface AllowedValues { String message(); String fieldName(); int fieldNumber(); String[] allowedValues() default {"Y", "N"}; String defaultValue() default ""; }
I want to be able to write a unit test class to test the direct logic in that validator. But it seems that most places I googled give examples of test classes where we basically test all validators for a given Model class, for example:
@BeforeClass public static void setup() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); } @Test public void testEmailExistsIncorrect() { Set<constraintviolation<usercredentialsdto>> violations = validator .validate(credentials, UserCredentialsDto.class); Assert.assertEquals(1, violations.size()); }
I don't want to build mock models to test all validators. Is there a way to create a separate test class for just testing the logic in one single validator directly without using any other model classes etc?
Implementing the Validator Interface A Validator implementation must contain a constructor, a set of accessor methods for any attributes on the tag, and a validate method, which overrides the validate method of the Validator interface.
Data validation is a common task that occurs in all layers of an application, including persistence. The Java™ Persistence API (JPA) 2.0 provides support for the Bean Validation API so that data validation can be done at run time.
The validation class implements the ConstraintValidator interface, and must also implement the isValid method; it's in this method that we defined our validation rules. Naturally, we're going with a simple validation rule here in order to show how the validator works.
Naturally, we're going with a simple validation rule here in order to show how the validator works. ConstraintValidator defines the logic to validate a given constraint for a given object. Implementations must comply with the following restrictions:
Jul 19 '20 at 19:25 @Kavithakaran Kanapathippillai: UserValidator is doing integration testing. I want to test NameValidator's isValid method through Junit Test cases as part of Unit testing. It isrequired for getting unit test code coverage.
You can then use this method to initialize the validator. You can also make this method just package visible, provided your test resides in the same package Place the test annotation somewhere into your test class and retrieve it via reflection in order to pass it to the initialize method. Use annotation proxies.
You can test the validator standalone. The rub is of course the initialize method, since it needs an instance of the annotation. You basically have three options:
AnnotationDescriptor
and AnnotationFactory
. The code would somewhat like this:--
private AllowedValues createAnnotation(String[]values, String defaultValue) { AnnotationDescriptor<AllowedValues> descriptor = new AnnotationDescriptor<AllowedValues>( AllowedValues.class ); descriptor.setValue( "values", values ); descriptor.setValue( "defaultValue", defaultValue ); return AnnotationFactory.create( descriptor ); }
You would need to depend on Hibernate Validator internal classes, but for testing purposes this should be fine. Of course you could also just create your own proxy framework.
I used the below pattern:
@RunWith(MockitoJUnitRunner.class) public class AllowedValuesValidatorTest { @Mock AllowedValuesValidator allowedValuesValidator; @Mock ConstraintValidatorContext constraintValidatorContext; @Before public void setUp() { doCallRealMethod().when(allowedValuesValidator).initialize(any()); when(allowedValuesValidator.isValid(any(), any())).thenCallRealMethod(); AllowedValuesValidatorTestClass testClass = new AllowedValuesValidatorTestClass(); allowedValuesValidator.initialize(testClass); } @Test public void testIsValidWithValidValues() { assertTrue(allowedValuesValidator.isValid("Value", constraintValidatorContext)); } private class AllowedValuesValidatorTestClass implements AllowedValues { @Override public String message() { return "Test Message"; } @Override public Class<?>[] groups() { return new Class[]{}; } @Override public Class<? extends Payload>[] payload() { return new Class[]{}; } @Override public Class<? extends Annotation> annotationType() { return AllowedValues.class; } } }
We can mock the class we are testing. As an annotation is just an interface we can pass in a concrete implementation as the parameter to initialise (which you can make behave any way you need in order to initialise your test correctly). You can then pass in a mock ConstraintValidatorContext
to your isValid
method. However, you may need to do some extra work depending on what that method does, if it interacts with the context you may need to do some further mocking.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With