Search code examples
javaspring-bootjunitjunit4customvalidator

Parameterized junit test of custom validation in springboot


I have this validator in springboot which gives error when an integer is not between 1 and 3 and i am using addConstraintViolation to print a message from properties file

public class SizeValidator implements ConstraintValidator<SizeValidation, Integer> {
    private int maxSize = 500;
    private int minSize = 1;

    @Override
    public void initialize(SizeValidation constraintAnnotation) {
        maxSize = constraintAnnotation.mxSize();
        minSize = constraintAnnotation.minSize();
    }

    @Override
    public boolean isValid(Integer givenSize, ConstraintValidatorContext context) {
        if (givenSize > maxSize || givenSize<= minPageSize) {
            addConstraintViolation(givenSize, "{Size.wrongSize.message}", context);
            return false;
        }
        return true;
    }

    private void addConstraintViolation(Integer givenSize, String errorMessage, ConstraintValidatorContext context) {
        final HibernateConstraintValidatorContext customContext = context.unwrap(HibernateConstraintValidatorContext.class);
        customContext.addExpressionVariable("givenSize", givenSize);

        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate(errorMessage)
                .addConstraintViolation();
    }
}

and in my validation.properties

Size.wrongSize.message=Value of size ${givenSize} should be between 1 and 3

i wanted to write a Parameterized junit test for it as following but it returns nullpointerexception where am i doing wrong please?

 java.lang.NullPointerException
      at sizeValidator.addConstraintViolation(SizeValidator.java:33)

intellij says its at position

context.buildConstraintViolationWithTemplate(errorMessage) .addConstraintViolation();

import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
    import org.junit.Test;
    import org.junit.experimental.runners.Enclosed;
    import org.junit.runner.RunWith;
    import org.junit.runners.Parameterized;
    
    import javax.validation.ConstraintValidatorContext;
    import java.util.Arrays;
    import java.util.Collection;
    
    import static org.junit.Assert.assertTrue;
    import static org.mockito.ArgumentMatchers.anyString;
    import static org.mockito.ArgumentMatchers.eq;
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.when;
    
        @RunWith(Enclosed.class)
        public class SizeValidatorTest{
        
            @RunWith(Parameterized.class)
            public static class TestValidEntries {
                private SizeValidator validator = new SizeValidator();
                private Integer val;
        
        
                public TestValidEntries(Integer val) {
                    super();
                    this.val = val;
                }
        
                @Test
                public void test() {
                    assertTrue(isValid(val));
                }
        
                @Parameterized.Parameters(name = "{index} Valid: {0}")
                public static Collection data() {
                    return Arrays.asList(
                            -1, 501
                    );
                }
        
                public boolean isValid(Integer value) {
                    final HibernateConstraintValidatorContext context = mock(HibernateConstraintValidatorContext.class);
                    when(context.unwrap(HibernateConstraintValidatorContext.class)).thenReturn(context);
                    when(context.addExpressionVariable(eq("nonUnique"), anyString())).thenReturn(context);
                    when(context.getDefaultConstraintMessageTemplate()).thenReturn("template");
                    final ConstraintValidatorContext.ConstraintViolationBuilder builder = mock(ConstraintValidatorContext.ConstraintViolationBuilder.class);
                    when(context.buildConstraintViolationWithTemplate("template")).thenReturn(builder);
        
                    when(builder.addPropertyNode(anyString())).thenReturn(mock(ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext.class));
        
                    return validator.isValid(value, context);
                }
        
            }
        }

Solution

  • The problem is in the configuration of the context mock.

    Your configuration says:

    when(context
          .buildConstraintViolationWithTemplate(
              "template"))
    .thenReturn(builder);
    

    But the string you pass in your code under test is: "{Size.wrongSize.message}" Because this string do not match the mock returns null.


    A better approach is to always use unspecific matchers like anyString() in the arrange part of the test method (or in setup) an specific matchers (or values) only in conjunction with Mockito.verify() in the assert part of the test method.