0
# Soft Assertions
1
2
Soft assertions collect multiple assertion failures and report them together instead of failing on the first error, enabling comprehensive validation in a single test.
3
4
## Core Imports
5
6
```java
7
import static org.assertj.core.api.Assertions.*;
8
import org.assertj.core.api.SoftAssertions;
9
import org.assertj.core.api.AutoCloseableSoftAssertions;
10
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension;
11
import org.junit.jupiter.api.extension.ExtendWith;
12
```
13
14
## Capabilities
15
16
### Basic Soft Assertions
17
18
Standard soft assertions that collect errors and report them all at once.
19
20
```java { .api }
21
class SoftAssertions {
22
// Constructor
23
SoftAssertions()
24
25
// All standard assertThat methods available
26
BooleanAssert assertThat(boolean actual)
27
IntegerAssert assertThat(int actual)
28
StringAssert assertThat(String actual)
29
ObjectAssert<T> assertThat(T actual)
30
ListAssert<T> assertThat(List<T> actual)
31
// ... all other assertThat overloads
32
33
// Soft assertion control
34
void assertAll()
35
List<AssertionError> assertionErrorsCollected()
36
boolean hasErrors()
37
boolean wasSuccess()
38
39
// Error message customization
40
SoftAssertions describedAs(String description)
41
SoftAssertions as(String description)
42
}
43
```
44
45
Usage examples:
46
```java
47
// Basic soft assertions usage
48
SoftAssertions softly = new SoftAssertions();
49
50
User user = getUser();
51
softly.assertThat(user.getName()).isNotNull();
52
softly.assertThat(user.getAge()).isPositive();
53
softly.assertThat(user.getEmail()).contains("@");
54
softly.assertThat(user.getRole()).isEqualTo(Role.ADMIN);
55
56
// This will throw AssertionError with ALL failures
57
softly.assertAll();
58
59
// Check if there were any errors without throwing
60
if (softly.hasErrors()) {
61
List<AssertionError> errors = softly.assertionErrorsCollected();
62
// Handle errors as needed
63
}
64
```
65
66
### Auto-Closeable Soft Assertions
67
68
Soft assertions that automatically call `assertAll()` when used in try-with-resources.
69
70
```java { .api }
71
class AutoCloseableSoftAssertions extends SoftAssertions implements AutoCloseable {
72
// Constructor
73
AutoCloseableSoftAssertions()
74
75
// AutoCloseable implementation
76
void close()
77
78
// Inherits all SoftAssertions methods
79
// Automatically calls assertAll() on close
80
}
81
```
82
83
Usage examples:
84
```java
85
// Try-with-resources automatically calls assertAll()
86
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
87
softly.assertThat(name).isNotBlank();
88
softly.assertThat(age).isBetween(18, 65);
89
softly.assertThat(email).matches("\\w+@\\w+\\.\\w+");
90
// assertAll() called automatically when exiting try block
91
}
92
93
// Validation method using auto-closeable soft assertions
94
public void validatePerson(Person person) {
95
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
96
softly.assertThat(person).isNotNull();
97
softly.assertThat(person.getName()).isNotEmpty();
98
softly.assertThat(person.getAge()).isGreaterThan(0);
99
} // assertAll() called here
100
}
101
```
102
103
### BDD Style Soft Assertions
104
105
Behavior-driven development style soft assertions using `then()` instead of `assertThat()`.
106
107
```java { .api }
108
class BDDSoftAssertions {
109
// Constructor
110
BDDSoftAssertions()
111
112
// BDD-style assertion methods (then instead of assertThat)
113
BooleanAssert then(boolean actual)
114
IntegerAssert then(int actual)
115
StringAssert then(String actual)
116
ObjectAssert<T> then(T actual)
117
ListAssert<T> then(List<T> actual)
118
// ... all other then() overloads
119
120
// Control methods
121
void assertAll()
122
List<AssertionError> assertionErrorsCollected()
123
}
124
125
class AutoCloseableBDDSoftAssertions extends BDDSoftAssertions implements AutoCloseable {
126
// Constructor
127
AutoCloseableBDDSoftAssertions()
128
129
// AutoCloseable implementation
130
void close()
131
}
132
```
133
134
Usage examples:
135
```java
136
// BDD-style soft assertions
137
BDDSoftAssertions softly = new BDDSoftAssertions();
138
139
// Given
140
Order order = createTestOrder();
141
142
// When
143
ProcessResult result = orderProcessor.process(order);
144
145
// Then
146
softly.then(result.isSuccess()).isTrue();
147
softly.then(result.getOrderId()).isNotNull();
148
softly.then(result.getTotal()).isGreaterThan(BigDecimal.ZERO);
149
softly.assertAll();
150
151
// Auto-closeable BDD style
152
try (AutoCloseableBDDSoftAssertions softly = new AutoCloseableBDDSoftAssertions()) {
153
softly.then(order.getItems()).isNotEmpty();
154
softly.then(order.getCustomer()).isNotNull();
155
softly.then(order.getStatus()).isEqualTo(OrderStatus.PENDING);
156
}
157
```
158
159
### JUnit 4 Integration
160
161
Soft assertions integrated with JUnit 4 using rules.
162
163
```java { .api }
164
class JUnitSoftAssertions extends SoftAssertions {
165
// JUnit 4 Rule support
166
static JUnitSoftAssertions assertSoftly()
167
}
168
169
class JUnitBDDSoftAssertions extends BDDSoftAssertions {
170
// JUnit 4 BDD Rule support
171
static JUnitBDDSoftAssertions assertSoftly()
172
}
173
```
174
175
Usage examples:
176
```java
177
// JUnit 4 with Rule (not commonly used in modern code)
178
public class MyTest {
179
@Rule
180
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
181
182
@Test
183
public void testValidation() {
184
softly.assertThat(value1).isPositive();
185
softly.assertThat(value2).isNotNull();
186
// assertAll() called automatically by rule
187
}
188
}
189
```
190
191
### JUnit 5 Integration
192
193
Modern JUnit 5 integration with extensions and parameter injection.
194
195
```java { .api }
196
class JUnitJupiterSoftAssertions extends SoftAssertions {
197
// Constructor
198
JUnitJupiterSoftAssertions()
199
}
200
201
class JUnitJupiterBDDSoftAssertions extends BDDSoftAssertions {
202
// Constructor
203
JUnitJupiterBDDSoftAssertions()
204
}
205
206
// Extension for automatic injection
207
class SoftAssertionsExtension implements ParameterResolver {
208
// Provides SoftAssertions instances to test methods
209
}
210
```
211
212
Usage examples:
213
```java
214
// Method 1: Extension with parameter injection
215
@ExtendWith(SoftAssertionsExtension.class)
216
class ValidationTest {
217
218
@Test
219
void shouldValidateUser(SoftAssertions softly) {
220
User user = createTestUser();
221
222
softly.assertThat(user.getName()).isNotBlank();
223
softly.assertThat(user.getEmail()).contains("@");
224
softly.assertThat(user.isActive()).isTrue();
225
226
softly.assertAll();
227
}
228
}
229
230
// Method 2: Manual instantiation
231
class UserTest {
232
233
@Test
234
void shouldValidateUserManually() {
235
JUnitJupiterSoftAssertions softly = new JUnitJupiterSoftAssertions();
236
237
User user = createTestUser();
238
softly.assertThat(user.getName()).startsWith("Test");
239
softly.assertThat(user.getCreatedDate()).isBeforeOrEqualTo(LocalDate.now());
240
241
softly.assertAll();
242
}
243
}
244
```
245
246
### Error Collection and Reporting
247
248
Advanced error handling and custom reporting.
249
250
```java { .api }
251
// Error collection methods
252
List<AssertionError> assertionErrorsCollected()
253
boolean hasErrors()
254
boolean wasSuccess()
255
String errorsAsString()
256
257
// Custom error handling
258
SoftAssertions onError(Consumer<AssertionError> errorHandler)
259
SoftAssertions collectErrors(boolean collect)
260
```
261
262
Usage examples:
263
```java
264
SoftAssertions softly = new SoftAssertions();
265
266
// Collect multiple validation errors
267
softly.assertThat(user.getName()).isNotNull();
268
softly.assertThat(user.getAge()).isBetween(0, 120);
269
softly.assertThat(user.getEmail()).matches(".*@.*\\..*");
270
271
// Check for errors without throwing
272
if (softly.hasErrors()) {
273
List<AssertionError> errors = softly.assertionErrorsCollected();
274
275
// Log each error individually
276
errors.forEach(error -> logger.error("Validation failed: {}", error.getMessage()));
277
278
// Or get all errors as string
279
String allErrors = softly.errorsAsString();
280
throw new ValidationException("Multiple validation errors: " + allErrors);
281
}
282
```
283
284
### Custom Soft Assertion Extensions
285
286
Creating custom soft assertions for domain objects.
287
288
```java { .api }
289
// Extending SoftAssertions for custom types
290
class CustomSoftAssertions extends SoftAssertions {
291
292
public UserAssert assertThat(User actual) {
293
return proxy(UserAssert.class, User.class, actual);
294
}
295
296
public OrderAssert assertThat(Order actual) {
297
return proxy(OrderAssert.class, Order.class, actual);
298
}
299
}
300
```
301
302
Usage examples:
303
```java
304
// Custom domain-specific soft assertions
305
class UserSoftAssertions extends SoftAssertions {
306
307
public UserAssert assertThat(User actual) {
308
return proxy(UserAssert.class, User.class, actual);
309
}
310
}
311
312
class UserAssert extends AbstractObjectAssert<UserAssert, User> {
313
314
public UserAssert(User actual) {
315
super(actual, UserAssert.class);
316
}
317
318
public UserAssert hasValidEmail() {
319
isNotNull();
320
if (!actual.getEmail().contains("@")) {
321
failWithMessage("Expected email to contain @ but was <%s>", actual.getEmail());
322
}
323
return this;
324
}
325
326
public UserAssert isAdult() {
327
isNotNull();
328
if (actual.getAge() < 18) {
329
failWithMessage("Expected user to be adult but age was <%d>", actual.getAge());
330
}
331
return this;
332
}
333
}
334
335
// Usage
336
UserSoftAssertions softly = new UserSoftAssertions();
337
softly.assertThat(user1).hasValidEmail().isAdult();
338
softly.assertThat(user2).hasValidEmail().isAdult();
339
softly.assertAll();
340
```
341
342
### Soft Assertion Best Practices
343
344
Common patterns and best practices for soft assertions.
345
346
```java { .api }
347
// Validation helper methods
348
static void validateUser(User user) {
349
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
350
softly.assertThat(user).isNotNull();
351
softly.assertThat(user.getName()).isNotBlank();
352
softly.assertThat(user.getAge()).isPositive();
353
softly.assertThat(user.getEmail()).contains("@");
354
}
355
}
356
357
// Batch validation
358
static void validateUsers(List<User> users) {
359
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
360
softly.assertThat(users).isNotEmpty();
361
362
for (int i = 0; i < users.size(); i++) {
363
User user = users.get(i);
364
softly.assertThat(user.getName())
365
.as("User[%d] name", i)
366
.isNotBlank();
367
softly.assertThat(user.getEmail())
368
.as("User[%d] email", i)
369
.contains("@");
370
}
371
}
372
}
373
```
374
375
## Types
376
377
```java { .api }
378
// Core soft assertion classes
379
abstract class AbstractSoftAssertions {
380
protected List<AssertionError> errors
381
protected boolean collectErrors
382
383
void assertAll()
384
List<AssertionError> assertionErrorsCollected()
385
}
386
387
class SoftAssertions extends AbstractSoftAssertions {
388
// Standard soft assertions implementation
389
}
390
391
class AutoCloseableSoftAssertions extends SoftAssertions implements AutoCloseable {
392
void close() // calls assertAll()
393
}
394
395
// BDD variants
396
class BDDSoftAssertions extends AbstractSoftAssertions {
397
// BDD-style methods using then() instead of assertThat()
398
}
399
400
class AutoCloseableBDDSoftAssertions extends BDDSoftAssertions implements AutoCloseable {
401
void close()
402
}
403
404
// JUnit integration classes
405
class JUnitSoftAssertions extends SoftAssertions {
406
// JUnit 4 integration
407
}
408
409
class JUnitJupiterSoftAssertions extends SoftAssertions {
410
// JUnit 5 integration
411
}
412
413
// Error handling
414
class AssertionError extends Error {
415
String getMessage()
416
Throwable getCause()
417
}
418
419
// Consumer for error handling
420
interface Consumer<T> {
421
void accept(T t);
422
}
423
```