エキサイト株式会社 メディア事業部エンジニアの中です。
カスタムバリデーションと@NotEmptyをつけてユニットテストする際、nullを設定すると NullPointerExceptionが発生することがあります。
理由は@NotEmptyと@CustomValidateの2回チェックが通るため、 CustomValidate側の方でnull対策をしておかないといけないからです。
ユースケース
- カスタムバリデーションを作った際のNullPointerExceptionの回避方法
データモデル
@Data @Accessors(chain = true) public class TestId { @NotEmpty @ExciteIdValidate private String id; }
カスタムバリデーション
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = {ExciteIdValidator.class}) public @interface ExciteIdValidate { String message() default "Idを正しく入力してください"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { ExciteIdValidate[] value(); } }
public class ExciteIdValidator implements ConstraintValidator<ExciteIdValidate, String> { private final static Pattern PATTERN_NG_WORD = Pattern.compile("ngword"); @Override public void initialize(ExciteIdValidate exciteIdValidate) { } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if(PATTERN_NG_WORD.matcher(value).matches()){ return false; } return true; } }
入力例
テストコードです
@ExtendWith(MockitoExtension.class) class TestIdTest { private static Validator validator; @BeforeAll public static void BeforeAll() { validator = Validation .buildDefaultValidatorFactory() .getValidator(); } @Test @Description("nullチェック") void ngNull() { final TestIdForm testId = new TestIdForm() .setId(null); assertFalse(validator.validate(testId).isEmpty()); } }
出力例 エラー部分抜粋しています。
Caused by: java.lang.NullPointerException at com.form.validate.TestIdValidator.isValid(TestIdValidator.java:27) at com.form.validate.TestIdValidator.isValid(TestIdValidator.java:7) at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:180) ... 99 more
改善
当たり前のことですが、nullを考慮して実装すれば問題なく通ります。 returnをfalseにしていますが、trueでも最終的な結果はエラーになります。 @NotEmptyでエラー判定されているからです。
public class ExciteIdValidator implements ConstraintValidator<ExciteIdValidate, String> { private final static Pattern PATTERN_NG_WORD = Pattern.compile("ngword"); @Override public void initialize(ExciteIdValidate exciteIdValidate) { } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if(value == null){ return false; } if(PATTERN_NG_WORD.matcher(value).matches()){ return false; } return true; } }