Generates unit tests for Jakarta Bean Validation (JSR-380) constraints like @NotNull, @Email, @Min with Hibernate Validator. Covers custom validators, violation assertions, groups, and parameterized tests without Spring.
From developer-kit-javanpx claudepluginhub giuseppe-trisciuoglio/developer-kit --plugin developer-kit-javaThis skill is limited to using the following tools:
references/advanced-patterns.mdreferences/custom-validators.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
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.
@Constraint validators and constraint violation messagesjakarta.validation-api and hibernate-validator in test scopeValidator once in @BeforeEach using Validation.buildDefaultValidatorFactory()getPropertyPath(), getMessage(), getInvalidValue()references/custom-validators.md for patterns@ParameterizedTestreferences/advanced-patterns.md)<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>
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();
}
}
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);
}
}
For custom constraint patterns, see references/custom-validators.md:
@Constraint annotationsConstraintValidatorFor validation groups and parameterized tests, see references/advanced-patterns.md:
groups parameter@ParameterizedTest with @ValueSource and @CsvSourceBaseValidationTest to share validator setup@NotNull for mandatory fields combined with other constraints@NotNull with other constraints for mandatory fieldsValidator instances are thread-safe and can be shared@Valid on nested objects for recursive validationtrue for null valuesValidatorFactory 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/custom-validators.mdreferences/advanced-patterns.md