PluginBench
Skill
Pass
Audit score 90

unit-test-bean-validation

giuseppe-trisciuoglio/developer-kit

Unit test Jakarta Bean Validation constraints and custom validators with JUnit 5 in isolation.

What is unit-test-bean-validation?

Provides executable patterns for testing Jakarta Bean Validation (JSR-380) annotations like @NotNull, @Email, @Min, @Max with Hibernate Validator. Covers built-in constraints, custom validators, cross-field validation, and validation groups without Spring context. Use when writing validation unit tests or testing custom constraint implementations.

  • Test built-in constraints (@NotNull, @Email, @Min, @Max, @Size) with assertion patterns
  • Test custom @Constraint validators and constraint violation messages
  • Extract and verify violation details (property path, message, invalid value)
  • Test cross-field validation and conditional validation with groups
  • Run parameterized validation tests efficiently with multiple inputs
  • Test validation logic in DTOs and request objects without Spring context

How to install unit-test-bean-validation

npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-bean-validation
Prerequisites
  • jakarta.validation-api dependency
  • hibernate-validator dependency (test scope)
  • JUnit 5
  • assertj-core for fluent assertions
Claude Code
Cursor
Windsurf
Cline

How to use unit-test-bean-validation

  1. 1.Add jakarta.validation-api and hibernate-validator to test dependencies
  2. 2.Create a base test class that builds Validator in @BeforeEach using Validation.buildDefaultValidatorFactory()
  3. 3.Write test cases for valid objects (should have no violations)
  4. 4.Write test cases for invalid objects and assert constraint violations
  5. 5.Extract violation details using getPropertyPath(), getMessage(), getInvalidValue()
  6. 6.Use @ParameterizedTest to test multiple inputs efficiently
  7. 7.For custom validators, implement ConstraintValidator and test with @Constraint annotation
  8. 8.For conditional validation, define validation groups and test with groups parameter

Use cases

Good for
  • Unit testing validation constraints on data transfer objects before service layer
  • Verifying custom validator implementations for domain-specific rules
  • Testing cross-field validation like password confirmation matching
  • Validating conditional constraints using validation groups
  • Testing edge cases (null, empty, boundary values) for constraint annotations
Who it's for
  • Java developers writing unit tests for validation logic
  • Backend engineers building DTOs and request objects with constraints
  • QA engineers verifying data integrity validation without integration tests
  • Teams using Jakarta Bean Validation or JSR-380 in their projects

unit-test-bean-validation FAQ

Do I need Spring to run these validation tests?

No. These tests run in isolation using Validator.validate() without Spring context, making them fast unit tests.

How do I test that null values are handled correctly?

Most constraints ignore null by default. Combine @NotNull with other constraints for mandatory fields, then test both null and invalid value cases separately.

Can I test custom validators with this skill?

Yes. Implement ConstraintValidator, annotate your constraint with @Constraint(validatedBy = YourValidator.class), and test using the same Validator.validate() pattern.

How do I verify the correct property path in constraint violations?

Use extracting(ConstraintViolation::getPropertyPath).extracting(Path::toString) to assert the field name matches expectations.

What's the difference between testing validation groups and regular constraints?

Validation groups allow conditional validation rules. Pass the group class to validator.validate(object, GroupClass.class) to test group-specific constraints.

Full instructions (SKILL.md)

Source of truth, from giuseppe-trisciuoglio/developer-kit.


name: unit-test-bean-validation description: Provides patterns for unit testing Jakarta Bean Validation (JSR-380), including @Valid, @NotNull, @Min, @Max, @Email constraints with Hibernate Validator. Generates custom validator tests, constraint violation assertions, validation groups, and parameterized validation tests. Validates data integrity logic without Spring context. Use when writing validation tests, bean validation tests, or testing custom constraint validators. allowed-tools: Read, Write, Bash, Glob, Grep

Unit Testing Jakarta Bean Validation

Overview

This skill provides executable patterns for unit testing Jakarta Bean Validation annotations and custom validators using JUnit 5. Covers built-in constraints (@NotNull, @Email, @Min, @Max, @Size), custom @Constraint implementations, cross-field validation, and validation groups. Tests run in isolation without Spring context.

When to Use

  • Writing unit tests for Jakarta Bean Validation or JSR-380 constraints
  • Testing custom @Constraint validators and constraint violation messages
  • Testing bean validation logic in DTOs and request objects
  • Verifying cross-field validation (e.g., password matching)
  • Testing conditional validation with validation groups
  • Fast validation tests without Spring Boot context

Instructions

  1. Add dependencies: Include jakarta.validation-api and hibernate-validator in test scope
  2. Create base test class: Build Validator once in @BeforeEach using Validation.buildDefaultValidatorFactory()
  3. Test valid cases first: Verify objects pass without violations
  4. Test invalid cases: Assert constraint violations include correct property path and message
  5. Extract violation details: Use getPropertyPath(), getMessage(), getInvalidValue()
  6. Test custom validators: See references/custom-validators.md for patterns
  7. Use parameterized tests: Test multiple inputs efficiently with @ParameterizedTest
  8. Group validation tests: Use validation groups for conditional rules (see references/advanced-patterns.md)

Examples

Maven Setup

<dependency>
  <groupId>jakarta.validation</groupId>
  <artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.assertj</groupId>
  <artifactId>assertj-core</artifactId>
  <scope>test</scope>
</dependency>

Common Test Setup

import jakarta.validation.*;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.path.Path;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

class BaseValidationTest {
  protected Validator validator;

  @BeforeEach
  void setUpValidator() {
    validator = Validation.buildDefaultValidatorFactory().getValidator();
  }
}

Testing Basic Constraints

class UserDtoTest extends BaseValidationTest {

  @Test
  void shouldPassValidationWithValidUser() {
    UserDto user = new UserDto("Alice", "alice@example.com", 25);
    assertThat(validator.validate(user)).isEmpty();
  }

  @Test
  void shouldFailWhenNameIsNull() {
    UserDto user = new UserDto(null, "alice@example.com", 25);
    assertThat(validator.validate(user))
      .extracting(ConstraintViolation::getMessage)
      .contains("must not be blank");
  }

  @Test
  void shouldFailWhenEmailIsInvalid() {
    UserDto user = new UserDto("Alice", "invalid-email", 25);
    Set<ConstraintViolation<UserDto>> violations = validator.validate(user);
    assertThat(violations)
      .extracting(ConstraintViolation::getPropertyPath)
      .extracting(Path::toString)
      .contains("email");
  }

  @Test
  void shouldFailWhenAgeIsBelowMinimum() {
    UserDto user = new UserDto("Alice", "alice@example.com", -1);
    assertThat(validator.validate(user))
      .extracting(ConstraintViolation::getMessage)
      .contains("must be greater than or equal to 0");
  }

  @Test
  void shouldFailWhenMultipleConstraintsViolated() {
    UserDto user = new UserDto(null, "invalid", -5);
    assertThat(validator.validate(user)).hasSize(3);
  }
}

Testing Custom Validators

For custom constraint patterns, see references/custom-validators.md:

  • Creating @Constraint annotations
  • Implementing ConstraintValidator
  • Cross-field validation (password matching)
  • Stateless validator best practices

Testing Validation Groups

For validation groups and parameterized tests, see references/advanced-patterns.md:

  • Defining validation group interfaces
  • Conditional validation with groups parameter
  • @ParameterizedTest with @ValueSource and @CsvSource
  • Debugging failed validation tests

Best Practices

  • Test both valid and invalid: Every constraint needs both passing and failing test cases
  • Assert violation details: Verify property path, message, and constraint type
  • Test edge cases: null, empty string, whitespace-only, boundary values
  • Keep validators stateless: Custom validators must not maintain state
  • Use clear messages: Constraint messages should be user-friendly
  • Group related tests: Extend BaseValidationTest to share validator setup
  • Test error messages: Ensure messages match requirements

Common Pitfalls

  • Forgetting to test null values (most constraints ignore null by default)
  • Not verifying the property path in constraint violations
  • Testing validation at service/controller level instead of unit level
  • Creating overly complex custom validators
  • Missing @NotNull for mandatory fields combined with other constraints

Constraints and Warnings

  • Null handling: Most constraints ignore null by default — combine @NotNull with other constraints for mandatory fields
  • Thread safety: Validator instances are thread-safe and can be shared
  • Message localization: Test with different locales if i18n is required
  • Cascading validation: Use @Valid on nested objects for recursive validation
  • Custom validators: Must be stateless and return true for null values
  • Test isolation: Validation unit tests should not depend on Spring context or database

Troubleshooting

ValidatorFactory not found: Ensure jakarta.validation-api and hibernate-validator are on test classpath.

Custom validator not invoked: Verify @Constraint(validatedBy = YourValidator.class) annotation is correct.

Null values pass validation: This is expected behavior — constraints ignore null unless @NotNull is present.

Wrong violation count: Use hasSize() to verify exact count, check all fields in the object.

Property path incorrect: Ensure the field, not the getter, has the constraint annotation.

References