Repository navigation
Getting Started
| Java (to consume the artifact) | 17+ (main classes are compiled with --release 17) |
| Java (to build this repository) | 25 (test sources compile with --release 25) |
| Jakarta Validation | 3.0 or 3.1 |
| AssertJ | 3.x from 3.27.7; 4.0.0-M1 tested as a milestone |
| Provider | any Jakarta Validation implementation on the test classpath; only Hibernate Validator is tested |
The artifact is meant for test scope. Its own two dependencies — jakarta.validation-api and assertj-core — are
provided, so nothing comes with it transitively: your build declares the API, AssertJ, and a provider.
<!-- this library -->
<dependency>
<groupId>io.github.jinahya</groupId>
<artifactId>assertj-validation</artifactId>
<version><!-- released version --></version>
<scope>test</scope>
</dependency>
<!-- the API; the application usually has it already -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.1.1</version>
<scope>provided</scope>
</dependency>
<!-- AssertJ -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.27.7</version>
<scope>test</scope>
</dependency>
<!-- a provider, and the expression language it interpolates messages with -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>9.1.4.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.expressly</groupId>
<artifactId>expressly</artifactId>
<version>6.0.0</version>
<scope>test</scope>
</dependency>These are the versions this repository's default profile (jakartaee-11) builds with. For Jakarta Validation 3.0
(Jakarta EE 10) the build uses jakarta.validation-api 3.0.2, Hibernate Validator 8.0.5.Final and Expressly
5.0.0. Hibernate Validator and Expressly move together: 9 ↔ 6, 8 ↔ 5. See Build and Profiles.
The examples below describe the working tree (
0.6.2-SNAPSHOT).assertThatConstraintViolationExceptionis not in0.6.1. See Published artifact.
The examples on this wiki are adapted from the repository's tests (src/test/java/…/example/user and
…/example/service). The central fixture is a User:
class User {
@NotBlank
private final String name;
@Max(127)
@Min(value = 60, groups = Senior.class)
@Max(value = 59, groups = Junior.class)
@PositiveOrZero
private final int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
}
interface Junior {}
interface Senior {}import static com.github.jinahya.assertj.validation.ValidationAssertions.*;
assertThatBean(new User("Jane", 28)).isValid(); // passes
assertThatBean(new User(null, 0)).isNotValid(); // passes: name is blank
assertThatBean(new User("John", -1)).isNotValid(); // passes: age is negative
assertThatBean(new User("Jane", 28)).hasValidProperty("name");
assertThatBean(new User(null, 0)).doesNotHaveValidProperty("name");A value can be checked against a property without building a bean:
assertThatProperty("Jane").isValidFor(User.class, "name"); // passes
assertThatProperty(" ").isNotValidFor(User.class, "name"); // passes
assertThatProperty(300).isNotValidFor(User.class, "age"); // passes: @Max(127)Groups are selected per assertion:
assertThatProperty(59)
.isValidFor(User.class, "age") // Default
.targetingGroups(Junior.class).isValidFor(User.class, "age")
.targetingGroups(Senior.class).isNotValidFor(User.class, "age");A failing validity assertion lists the bean, the groups, and every violation:
The set of constraint violations resulted while validating
actual: User@3c1{name=null,age=0}
targeting
groups: [interface jakarta.validation.groups.Default]
expected to be empty but contains 1 element(s)
-> message : must not be blank
propertyPath : name
rootBeanClass : class …User
messageTemplate: {jakarta.validation.constraints.NotBlank.message}
What was validated comes first, then the expectation and one -> entry per violation.
The fragments pinned by User_FailureMessages_Test are the description text, the groups, the count, and the
propertyPath / rootBeanClass / messageTemplate lines. The message line is interpolated and depends on the
locale.
The failure goes through the assertion itself, so what you configure on it is honoured. A description set with
as(…) prefixes the text, in AssertJ's brackets, and a message set with withFailMessage(…) replaces it, the lazy
Supplier form being evaluated only on failure:
assertThatBean(user).as("registration case 42").isValid(); // [registration case 42] The set of …
assertThatBean(user).withFailMessage("registration rejected").isValid(); // registration rejectedThe isValid(Consumer) / isNotValid(Consumer) overloads hand the violation set to a consumer, which can use the
inspection assertions:
assertThatBean(new User(null, -1)).isNotValid(violations ->
assertThatConstraintViolations(violations)
.hasSize(2)
.filteredOn(v -> v.getPropertyPath().toString().equals("name"))
.first()
.hasMessageTemplate("{jakarta.validation.constraints.NotBlank.message}"));The consumer runs at a different point in different methods today; see Consumers. See Inspecting Violations for the violation assertions.
With no configuration, each assertion builds a ValidatorFactory and closes it before returning. A suite that wants
to avoid that cost passes its own:
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
assertThatBean(user).usingValidatorFactory(factory).isValid();
assertThatBean(other).usingValidatorFactory(factory).isNotValid();
} // the caller closes it, hereSee Validator Lifecycle.
assertj-validation · Apache License 2.0 · io.github.jinahya:assertj-validation · Jakarta Validation 3.0 / 3.1 · AssertJ 3.27+ · Java 17+
Running validation
Inspecting results
Reference
External