Search code examples
javaspring-bootvalidationjunit5hibernate-validator

Junit test cases for javax Custom Validator


I am working on cross field validation using javax validation API in Spring Boot Application. I have a User bean and i have to validate that both firstname and lastname are not null/empty. At least one of this field should have a value.

I have created custom annotation (NameMatch.java) and custom Validator (NameValidator.java) for this requirement.

@NameMatch(first = "firstname", second = "lastname", message = "The first and lastname can't be null")
public class User  {

    private String firstname;

    private String lastname;

    @NotNull
    @Email
    private String email;

    @NotNull
    private String phone;
}

NameMatch.java

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = NameValidator.class)
@Documented
public @interface NameMatch
{
    String message() default "{constraints.fieldmatch}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * @return The first field
     */
    String first();

    /**
     * @return The second field
     */
    String second();
}

NameValidator.java

public class NameValidator implements ConstraintValidator<NameMatch, Object>
{
    private String firstFieldName;
    private String secondFieldName;

    @Override
    public void initialize(final NameMatch constraintAnnotation)
    {
        firstFieldName = constraintAnnotation.first();
        secondFieldName = constraintAnnotation.second();
    }

    @Override
    public boolean isValid(final Object value, final ConstraintValidatorContext context)
    {
        boolean isValidName = false;
        try
        {
            final Object firstName = BeanUtils.getProperty(value, firstFieldName);
            final Object lastName = BeanUtils.getProperty(value, secondFieldName);
            
            // Validation logic

        }
        catch (final Exception ignore)
        {
        }
        return isValidName;
    }
}

UserValidator.java

public class UserValidator
{
    public void isValidUser()
    {
        //Create ValidatorFactory which returns validator
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
         
        //It validates bean instances
        Validator validator = factory.getValidator();
 
        User user = new User();
        user.setEmail("test@gmail.com");
        user.setPhone("12345678") 
 
        //Validate bean
        Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
 
        //Show errors
        if (constraintViolations.size() > 0) {
            for (ConstraintViolation<User> violation : constraintViolations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("Valid Object");
        }
    }
}

I have to write JUnit test cases for the Custom Validator class. I explored hibernate validator docs but couldn't find a way to invoke custom validator method through JUnit. Can someone please help to write JUnit test cases for above scenario.


Solution

  • Your NameValidator has public methods, so you can instantiate an object and write unit tests like for any other public method.

    A possible JUnit 5 test with Mockito can look like the following:

    @ExtendWith(MockitoExtension.class)
    class NameValidatorTest {
    
      @Mock
      private NameMatch nameMatch;
    
      @Mock
      private ConstraintValidatorContext constraintValidatorContext;
    
      @Test
      public void testIsValid() {
    
        when(nameMatch.first()).thenReturn("firstname");
        when(nameMatch.second()).thenReturn("lastname");
    
    
        NameValidator nameValidator = new NameValidator();
        nameValidator.initialize(nameMatch);
    
        User user = new User();
        user.setFirstname("Duke");
        user.setLastname("Duke");
    
        boolean result = nameValidator.isValid(user, constraintValidatorContext);
        assertTrue(result);
      }
    
    }
    

    Depending of what you need the ConstraintValidatorContext you might also want to mock methods or later verify that specific methods were invoked.

    If you are not using JUnit 5, you can adjust the code to not the JUnit 5 MockitoExtension and create the mocks using Mockito.mock().