A memo of the procedure from writing a self-made validator with Bean Validation to writing a unit test.
I will try to make a validator to judge whether it is a Michael Jackson song name.
package com.example.customvalidate;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.ArrayList;
import java.util.List;
public class MJValidator implements ConstraintValidator<MJ, String> {
    private final static List<String> songs = new ArrayList<String>() {
        {
            add("Remember The Time"); add("Stranger In Moscow"); add("You Are Not Alone");
        }
    };
    public void initialize(MJ constraintAnnotation) {
    }
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return songs.stream().anyMatch(song -> song.equals(value));
    }
}
--Inherit the ConstraintValidator interface. For the generics, specify the annotation to be created later and the type to be validated.
--ʻIsValid` method is used to judge the actual validation. Makes an arbitrary judgment and returns boolean.
--In this example, "Remember The Time", "Stranger In Moscow", or "You Are Not Alone" will pass the validation.
Make an annotation. Now you can use it like @ MJ
package com.example.customvalidate;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = {MJValidator.class})
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MJ {
    String message() default "you must specify one of Michael Jackson's songs.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        MJ[] value();
    }
}
--Specify the class to actually validate in place of @Constraint (validatedBy =). Specify the MJ Validator created above.
--For @Target, specify the target to which this annotation can be added. This time, I used arguments and fields.
--For message, specify the message to be set in the exception object when validation is caught.
package com.example.customvalidate;
import org.junit.Before;
import org.junit.Test;
import javax.validation.*;
import java.util.Set;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class MJValidatorTest {
    private Validator validator;
    @Before
    public void setUp() {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }
    @Test
public void An error will occur if you pass an incorrect song title.() {
        TestBean bean = new TestBean("Remember a Time");
        Set<ConstraintViolation<Object>> violations = validator.validate(bean);
        assertThat(violations.isEmpty(), is(false));
        String expectedMessage = "you must specify one of Michael Jackson's songs.";
        violations.forEach(v -> assertThat(v.getMessage().equals(expectedMessage), is(true)));
    }
    @Test
public void Do not cause an error when passing the correct song title() {
        TestBean bean = new TestBean("Remember The Time");
        Set<ConstraintViolation<Object>> violations = validator.validate(bean);
        assertThat(violations.isEmpty(), is(true));
    }
    private static class TestBean {
        @MJ
        private String song;
        TestBean(String song) {
            this.song = song;
        }
    }
}
Run it and if the test passes ok.
--Create an appropriate class as the inner class of the test class and set the annotation created this time in the field. --Running validation for an instance of that class
Recommended Posts