1. Introduction
In this article we will get acquainted with Soft Assertions in AssertJ, consider in detail why they are needed and discuss similar solutions in other frameworks for testing.
2. Motivation
First of all, we’ll understand why Soft Assertions are needed. Let’s take the following example:
@Test
void test_HardAssertions() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
Assertions.assertThat(result.getId()).isNull();
Assertions.assertThat(result.getType()).isEqualTo(1);
Assertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
}
This code checks whether the mapper correctly converts the Request object to DomainModel. We expect all assertions to pass if the mapper works correctly.
Now let’s imagine that the mapper contains a defect and displays id and status incorrectly. In this case, running the test will result in AssertionFailedError
:
org.opentest4j.AssertionFailedError:
expected: null
but was: "73a3f292-8131-4aa9-8d55-f0dba77adfdb"
It’s great that we can see why the test didn’t pass. But there is a nuance: we know that the id field was marked incorrectly, but we learn about the fact that the status field was not marked as we expected only when we run the test again. The reason for this is the Hard Assertions that we use in AssertJ. In other words, the first failed check causes AssertionError immediately, and subsequent checks are not performed.
At first glance, it seems that this is a small problem: we would run the test twice – first to detect an incorrect display of the id, and then to see a similar error with the status field. However, when our mapper is more complex and works with dozens of fields (as usual in real projects), constant restart of tests will take a lot of time. To solve this problem, AssertJ offers Soft Assertions.
3. Soft Assertions in AssertJ
Soft assertions solve this problem by collecting all errors into a single report. This approach simplifies debugging and saves time. Let’s see how soft assertions work in AssertJ by rewriting our test:
@Test
void test_softAssertions() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
SoftAssertions.assertSoftly(softAssertions -> {
softAssertions.assertThat(result.getId()).isNull()
softAssertions.assertThat(result.getType()).isEqualTo(1);
softAssertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
});
}
AssertJ allows you to execute a set of assertions gently: all checks in the specified lambda expression will be performed, even if some of them cause errors. After completing the test, all errors will be collected in one general report, as shown below:
org.assertj.core.error.AssertJMultipleFailuresError:
Multiple Failures (3 failures)
-- failure 1 --
expected: null
but was: "66f8625c-b5e4-4705-9a49-94db3b347f72"
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:19)
-- failure 2 --
expected: 1
but was: 0
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:20)
-- failure 3 --
expected: "DRAFT"
but was: "NEW"
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:21)
This AssertJ feature is very useful when debugging, as it reduces the time to search for bugs. It is also worth mentioning that there is another way to write tests with soft assertions in AssertJ.
@Test
void test_softAssertionsViaInstanceCreation() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(result.getId()).isNull();
softAssertions.assertThat(result.getType()).isEqualTo(1);
softAssertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
softAssertions.assertAll();
}
In this case, we create an instance of the SoftAssertions class directly, unlike the previous example, where the instance is created hidden, using the framework.
Both approaches are identical in functionality, so you can use any of them at your discretion.
4. Soft Assertions in other test frameworks
Since soft assertions are very useful, many testing frameworks have already implemented this functionality. However, in different frameworks it may be called differently or not have a name at all. For example, Junit5 has an assertAll() method that works similarly, but has its own peculiarities.TestNG also has Soft Assertions, the implementation of which is very similar to that in AssertJ. In general, most popular testing frameworks support soft assertions, even if they are called otherwise.
5. Let’s sum up
Let’s summarise the differences and similarities between hard and soft assertions in one table:
Hard Assertions | Soft Assertions | |
Is the default mode of operation of assertion | yes | No |
Supported by most frameworks (including AssertJ) | yes | yes |
Demonstrates trouble-free behavior | yes | No |
6. Conclusion
In this article, we reviewed soft assertions and their advantages. Unlike hard assertions, which interrupt the test on error, soft assertions allow you to continue the test, even if some asserts fail, after which we will receive a detailed error report. This approach greatly facilitates and speeds up debugging.
It is worth noting that soft assertions are not unique features of AssertJ – similar features are available in other popular frameworks, although they may be called differently or not have a special name at all.
About The Author: Yotec Team
More posts by Yotec Team