CtrlK
BlogDocsLog inGet started
Tessl Logo

giuseppe-trisciuoglio/developer-kit

Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.

89

Quality

89%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

Overview
Quality
Evals
Security
Files

custom-validators.mdplugins/developer-kit-java/skills/unit-test-bean-validation/references/

Custom Validators Testing Reference

Creating Custom Constraints

Annotation Definition

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface ValidPhoneNumber {
  String message() default "invalid phone number format";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}

Validator Implementation

public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
  private static final String PHONE_PATTERN = "^\\d{3}-\\d{3}-\\d{4}$";

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    if (value == null) return true; // null handled by @NotNull
    return value.matches(PHONE_PATTERN);
  }
}

Unit Test

class PhoneNumberValidatorTest extends BaseValidationTest {

  @Test
  void shouldAcceptValidPhoneNumber() {
    Contact contact = new Contact("Alice", "555-123-4567");
    assertThat(validator.validate(contact)).isEmpty();
  }

  @Test
  void shouldRejectInvalidFormat() {
    Contact contact = new Contact("Alice", "5551234567");
    assertThat(validator.validate(contact))
      .extracting(ConstraintViolation::getMessage)
      .contains("invalid phone number format");
  }

  @Test
  void shouldAllowNull() {
    Contact contact = new Contact("Alice", null);
    assertThat(validator.validate(contact)).isEmpty();
  }
}

Cross-Field Validation

Password Match Example

@PasswordsMatch
public class ChangePasswordRequest {
  private String newPassword;
  private String confirmPassword;
}

@Constraint(validatedBy = PasswordMatchValidator.class)
public @interface PasswordsMatch {
  String message() default "passwords do not match";
  Class<?>[] groups() default {};
}

public class PasswordMatchValidator
    implements ConstraintValidator<PasswordsMatch, ChangePasswordRequest> {
  @Override
  public boolean isValid(ChangePasswordRequest value, ConstraintValidatorContext context) {
    if (value == null) return true;
    return value.getNewPassword().equals(value.getConfirmPassword());
  }
}

Test

class PasswordValidationTest extends BaseValidationTest {

  @Test
  void shouldPassWhenPasswordsMatch() {
    var request = new ChangePasswordRequest("pass123", "pass123");
    assertThat(validator.validate(request)).isEmpty();
  }

  @Test
  void shouldFailWhenPasswordsDoNotMatch() {
    var request = new ChangePasswordRequest("pass123", "different");
    assertThat(validator.validate(request))
      .extracting(ConstraintViolation::getMessage)
      .contains("passwords do not match");
  }
}

Best Practices

  • Keep validators stateless - no instance fields
  • Return true for null values - let @NotNull handle null checks
  • Provide clear error messages in annotations
  • Test both valid and invalid cases
  • Verify property path and message in assertions

plugins

developer-kit-java

skills

unit-test-bean-validation

README.md

CHANGELOG.md

context7.json

CONTRIBUTING.md

README_CN.md

README_ES.md

README_IT.md

README.md

tessl.json

tile.json