Fluent assertion library providing rich assertions for Java tests with expressive failure messages
—
Soft assertions collect multiple assertion failures and report them together instead of failing on the first error, enabling comprehensive validation in a single test.
import static org.assertj.core.api.Assertions.*;
import org.assertj.core.api.SoftAssertions;
import org.assertj.core.api.AutoCloseableSoftAssertions;
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension;
import org.junit.jupiter.api.extension.ExtendWith;Standard soft assertions that collect errors and report them all at once.
class SoftAssertions {
// Constructor
SoftAssertions()
// All standard assertThat methods available
BooleanAssert assertThat(boolean actual)
IntegerAssert assertThat(int actual)
StringAssert assertThat(String actual)
ObjectAssert<T> assertThat(T actual)
ListAssert<T> assertThat(List<T> actual)
// ... all other assertThat overloads
// Soft assertion control
void assertAll()
List<AssertionError> assertionErrorsCollected()
boolean hasErrors()
boolean wasSuccess()
// Error message customization
SoftAssertions describedAs(String description)
SoftAssertions as(String description)
}Usage examples:
// Basic soft assertions usage
SoftAssertions softly = new SoftAssertions();
User user = getUser();
softly.assertThat(user.getName()).isNotNull();
softly.assertThat(user.getAge()).isPositive();
softly.assertThat(user.getEmail()).contains("@");
softly.assertThat(user.getRole()).isEqualTo(Role.ADMIN);
// This will throw AssertionError with ALL failures
softly.assertAll();
// Check if there were any errors without throwing
if (softly.hasErrors()) {
List<AssertionError> errors = softly.assertionErrorsCollected();
// Handle errors as needed
}Soft assertions that automatically call assertAll() when used in try-with-resources.
class AutoCloseableSoftAssertions extends SoftAssertions implements AutoCloseable {
// Constructor
AutoCloseableSoftAssertions()
// AutoCloseable implementation
void close()
// Inherits all SoftAssertions methods
// Automatically calls assertAll() on close
}Usage examples:
// Try-with-resources automatically calls assertAll()
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
softly.assertThat(name).isNotBlank();
softly.assertThat(age).isBetween(18, 65);
softly.assertThat(email).matches("\\w+@\\w+\\.\\w+");
// assertAll() called automatically when exiting try block
}
// Validation method using auto-closeable soft assertions
public void validatePerson(Person person) {
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
softly.assertThat(person).isNotNull();
softly.assertThat(person.getName()).isNotEmpty();
softly.assertThat(person.getAge()).isGreaterThan(0);
} // assertAll() called here
}Behavior-driven development style soft assertions using then() instead of assertThat().
class BDDSoftAssertions {
// Constructor
BDDSoftAssertions()
// BDD-style assertion methods (then instead of assertThat)
BooleanAssert then(boolean actual)
IntegerAssert then(int actual)
StringAssert then(String actual)
ObjectAssert<T> then(T actual)
ListAssert<T> then(List<T> actual)
// ... all other then() overloads
// Control methods
void assertAll()
List<AssertionError> assertionErrorsCollected()
}
class AutoCloseableBDDSoftAssertions extends BDDSoftAssertions implements AutoCloseable {
// Constructor
AutoCloseableBDDSoftAssertions()
// AutoCloseable implementation
void close()
}Usage examples:
// BDD-style soft assertions
BDDSoftAssertions softly = new BDDSoftAssertions();
// Given
Order order = createTestOrder();
// When
ProcessResult result = orderProcessor.process(order);
// Then
softly.then(result.isSuccess()).isTrue();
softly.then(result.getOrderId()).isNotNull();
softly.then(result.getTotal()).isGreaterThan(BigDecimal.ZERO);
softly.assertAll();
// Auto-closeable BDD style
try (AutoCloseableBDDSoftAssertions softly = new AutoCloseableBDDSoftAssertions()) {
softly.then(order.getItems()).isNotEmpty();
softly.then(order.getCustomer()).isNotNull();
softly.then(order.getStatus()).isEqualTo(OrderStatus.PENDING);
}Soft assertions integrated with JUnit 4 using rules.
class JUnitSoftAssertions extends SoftAssertions {
// JUnit 4 Rule support
static JUnitSoftAssertions assertSoftly()
}
class JUnitBDDSoftAssertions extends BDDSoftAssertions {
// JUnit 4 BDD Rule support
static JUnitBDDSoftAssertions assertSoftly()
}Usage examples:
// JUnit 4 with Rule (not commonly used in modern code)
public class MyTest {
@Rule
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
@Test
public void testValidation() {
softly.assertThat(value1).isPositive();
softly.assertThat(value2).isNotNull();
// assertAll() called automatically by rule
}
}Modern JUnit 5 integration with extensions and parameter injection.
class JUnitJupiterSoftAssertions extends SoftAssertions {
// Constructor
JUnitJupiterSoftAssertions()
}
class JUnitJupiterBDDSoftAssertions extends BDDSoftAssertions {
// Constructor
JUnitJupiterBDDSoftAssertions()
}
// Extension for automatic injection
class SoftAssertionsExtension implements ParameterResolver {
// Provides SoftAssertions instances to test methods
}Usage examples:
// Method 1: Extension with parameter injection
@ExtendWith(SoftAssertionsExtension.class)
class ValidationTest {
@Test
void shouldValidateUser(SoftAssertions softly) {
User user = createTestUser();
softly.assertThat(user.getName()).isNotBlank();
softly.assertThat(user.getEmail()).contains("@");
softly.assertThat(user.isActive()).isTrue();
softly.assertAll();
}
}
// Method 2: Manual instantiation
class UserTest {
@Test
void shouldValidateUserManually() {
JUnitJupiterSoftAssertions softly = new JUnitJupiterSoftAssertions();
User user = createTestUser();
softly.assertThat(user.getName()).startsWith("Test");
softly.assertThat(user.getCreatedDate()).isBeforeOrEqualTo(LocalDate.now());
softly.assertAll();
}
}Advanced error handling and custom reporting.
// Error collection methods
List<AssertionError> assertionErrorsCollected()
boolean hasErrors()
boolean wasSuccess()
String errorsAsString()
// Custom error handling
SoftAssertions onError(Consumer<AssertionError> errorHandler)
SoftAssertions collectErrors(boolean collect)Usage examples:
SoftAssertions softly = new SoftAssertions();
// Collect multiple validation errors
softly.assertThat(user.getName()).isNotNull();
softly.assertThat(user.getAge()).isBetween(0, 120);
softly.assertThat(user.getEmail()).matches(".*@.*\\..*");
// Check for errors without throwing
if (softly.hasErrors()) {
List<AssertionError> errors = softly.assertionErrorsCollected();
// Log each error individually
errors.forEach(error -> logger.error("Validation failed: {}", error.getMessage()));
// Or get all errors as string
String allErrors = softly.errorsAsString();
throw new ValidationException("Multiple validation errors: " + allErrors);
}Creating custom soft assertions for domain objects.
// Extending SoftAssertions for custom types
class CustomSoftAssertions extends SoftAssertions {
public UserAssert assertThat(User actual) {
return proxy(UserAssert.class, User.class, actual);
}
public OrderAssert assertThat(Order actual) {
return proxy(OrderAssert.class, Order.class, actual);
}
}Usage examples:
// Custom domain-specific soft assertions
class UserSoftAssertions extends SoftAssertions {
public UserAssert assertThat(User actual) {
return proxy(UserAssert.class, User.class, actual);
}
}
class UserAssert extends AbstractObjectAssert<UserAssert, User> {
public UserAssert(User actual) {
super(actual, UserAssert.class);
}
public UserAssert hasValidEmail() {
isNotNull();
if (!actual.getEmail().contains("@")) {
failWithMessage("Expected email to contain @ but was <%s>", actual.getEmail());
}
return this;
}
public UserAssert isAdult() {
isNotNull();
if (actual.getAge() < 18) {
failWithMessage("Expected user to be adult but age was <%d>", actual.getAge());
}
return this;
}
}
// Usage
UserSoftAssertions softly = new UserSoftAssertions();
softly.assertThat(user1).hasValidEmail().isAdult();
softly.assertThat(user2).hasValidEmail().isAdult();
softly.assertAll();Common patterns and best practices for soft assertions.
// Validation helper methods
static void validateUser(User user) {
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
softly.assertThat(user).isNotNull();
softly.assertThat(user.getName()).isNotBlank();
softly.assertThat(user.getAge()).isPositive();
softly.assertThat(user.getEmail()).contains("@");
}
}
// Batch validation
static void validateUsers(List<User> users) {
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
softly.assertThat(users).isNotEmpty();
for (int i = 0; i < users.size(); i++) {
User user = users.get(i);
softly.assertThat(user.getName())
.as("User[%d] name", i)
.isNotBlank();
softly.assertThat(user.getEmail())
.as("User[%d] email", i)
.contains("@");
}
}
}// Core soft assertion classes
abstract class AbstractSoftAssertions {
protected List<AssertionError> errors
protected boolean collectErrors
void assertAll()
List<AssertionError> assertionErrorsCollected()
}
class SoftAssertions extends AbstractSoftAssertions {
// Standard soft assertions implementation
}
class AutoCloseableSoftAssertions extends SoftAssertions implements AutoCloseable {
void close() // calls assertAll()
}
// BDD variants
class BDDSoftAssertions extends AbstractSoftAssertions {
// BDD-style methods using then() instead of assertThat()
}
class AutoCloseableBDDSoftAssertions extends BDDSoftAssertions implements AutoCloseable {
void close()
}
// JUnit integration classes
class JUnitSoftAssertions extends SoftAssertions {
// JUnit 4 integration
}
class JUnitJupiterSoftAssertions extends SoftAssertions {
// JUnit 5 integration
}
// Error handling
class AssertionError extends Error {
String getMessage()
Throwable getCause()
}
// Consumer for error handling
interface Consumer<T> {
void accept(T t);
}Install with Tessl CLI
npx tessl i tessl/maven-org-assertj--assertj-core