Search code examples
javaassertj

AssertJ's "soft" assertions assert hard on fail() invocations


Here's a code:

package com.example;

import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;

import java.util.List;

public class SoftAssertionsTest {
    @Test
    void testSoftAssertions() {
        List.of(1, 2, 3).forEach(element ->
                SoftAssertions.assertSoftly(softAssertions ->
                        softAssertions.fail("Ka-boom! Index: " + element)));
    }
}

I expected something along the lines of:

Ka-boom! Index: 1
Ka-boom! Index: 2
Ka-boom! Index: 3

Instead, AssertJ quit after the very first assertion error:

org.assertj.core.error.AssertJMultipleFailuresError: 
Multiple Failures (1 failure)
-- failure 1 --Ka-boom! Index: 1
at SoftAssertionsTest.lambda$testSoftAssertions$0(SoftAssertionsTest.java:13)

    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at by.afinny.credit.unit.repository.SoftAssertionsTest.lambda$testSoftAssertions$1(SoftAssertionsTest.java:12)
    at java.base/java.lang.Iterable.forEach(Iterable.java:75)

Why? Is there some way to manually trigger soft assertion errors in AssertJ?

In case you're wondering why I need that, imagine I have a list that expect to be empty, but if it's not, I want to assert on each element with dynamic fail messages (real case)


Solution

  • SoftAssertions.assertSoftly fails if any of the inner assertions failed (the assertions inside the lambda). You are calling assertSoftly 3 times with only a single inner assertion.

    Your code is equivalent to:

    @Test
    void testSoftAssertions() {
      SoftAssertions.assertSoftly(soft -> soft.fail("Ka-boom! Index: 1"));
      SoftAssertions.assertSoftly(soft -> soft.fail("Ka-boom! Index: 2"));
      SoftAssertions.assertSoftly(soft -> soft.fail("Ka-boom! Index: 3"));
    }
    

    But you want:

    @Test
    void testSoftAssertions() {
      SoftAssertions.assertSoftly(soft -> {
          soft.fail("Ka-boom! Index: 1");
          soft.fail("Ka-boom! Index: 2");
          soft.fail("Ka-boom! Index: 3");
      });
    }
    

    Thus, you need to move the loop into your assertSoftly call:

    @Test
    void testSoftAssertions() {
      SoftAssertions.assertSoftly(softAssertions -> {
          List.of(1, 2, 3).forEach(element ->
              softAssertions.fail("Ka-boom! Index: " + element));
      });
    }