Thursday, November 23, 2017

JUnit 5 meets AssertJ

JUnit 5 brings a lot of improvements in the assertions library, mainly thanks to Java 8 and Lambda Expression support and thanks to the presence of the new assertions like assertAll, assertTimeout orassertThrows. Although I really like JUnit 5 I believe that AssertJ is still a must in production grade unit tests and I will continue using it.

But I think there are potential scenarios of mixing both JUnit 5 and AssertJ in single unit test: one of them is mixing JUnit assertAll with AssertJ assertThat.

JUnit 5 - assertAll

Assertions.assertAll asserts that all supplied executables do not throw exceptions:

List<String> owners = Arrays.asList("Betty Davis", "Eduardo Rodriquez");

// assert
assertAll(
    () -> assertTrue(owners.contains("Betty Doe"), "Contains Betty Doe"),
    () -> assertTrue(owners.contains("John Doe"), "Contains John Doe"),
    () -> assertTrue(owners.contains("Eduardo Rodriquez"), "Eduardo Rodriquez")
);

The above will report 2 errors:

org.opentest4j.MultipleFailuresError: Multiple Failures (2 failures)
    Contains Betty Doe ==> expected: <true> but was: <false>
    Contains John Doe ==> expected: <true> but was: <false>

assertAll executes all passed executables and makes sure all pass (do not throw exception). In other words, assertAll allows grouped assertions.

In addition, assertAll can be used for creating dependent assertions:

List<String> owners = Arrays.asList("Betty Davis", "Eduardo Rodriquez");

// assert
assertAll(
    () -> {
        assertTrue(owners.contains("Betty Doe"), "Contains Betty Doe");

        assertAll(
            () -> assertNotNull(owners),
            () -> assertTrue(owners.size() > 1)
        );
    }
);

In the above example, when the first assertTrue fails the subsequent assertAll will be skipped.

AssertJ - SoftAssertions

Note: I wrote more about SoftAssertions in this article: AssertJ soft assertions - do we need them?

AssertJ offers SoftAssertions which basically do the same as JUnit 5 assertAll with the slight difference of not supporting dependent assertions.

List<String> owners = Arrays.asList("Betty Davis", "Eduardo Rodriquez");

assertSoftly(
    softAssertions -> {
        softAssertions.assertThat(owners).contains("Betty Doe");
        softAssertions.assertThat(owners).contains("John Doe");
        softAssertions.assertThat(owners).contains("Eduardo Rodriquez");
    }
);

The reported errors:

1) 
Expecting:
 <["Betty Davis", "Eduardo Rodriquez"]>
to contain:
 <["Betty Doe"]>
but could not find:
 <["Betty Doe"]>

at AssertJAssertionsTest.lambda$assertsSoftly$0(AssertJAssertionsTest.java:26)
2) 
Expecting:
 <["Betty Davis", "Eduardo Rodriquez"]>
to contain:
 <["John Doe"]>
but could not find:
 <["John Doe"]>

Mixing JUnit assertAll with AssertJ assertThat

Mixing JUnit 5 assertAll with AssertJ assertThat assertions seems to be like a nice option:

// arrange
String givenName = "Jean";
String expectedCity = "Monona";
String expectedAddress = "105 N. Lake St.";

// act
Optional<Owner> result = testObj.findByName(givenName);

// assert
assertThat(result).isPresent();

assertAll(
    () -> assertThat(result.get().getFirstName()).isEqualTo(givenName),
    () -> assertThat(result.get().getCity()).isEqualTo(expectedCity),
    () -> assertThat(result.get().getAddress()).isEqualTo(expectedAddress)
);

On the other hand, assertAll can be used as a parameter to assertThat:

// arrange
String givenName = "Jean";
String expectedCity = "Monona";
String expectedAddress = "105 N. Lake St.";

// act
Optional<Owner> result = testObj.findByName(givenName);

// assert
assertThat(result).hasValueSatisfying(owner -> assertAll(
    () -> assertThat(owner.getFirstName()).isEqualTo(givenName),
    () -> assertThat(owner.getCity()).isEqualTo(expectedCity),
    () -> assertThat(owner.getAddress()).isEqualTo(expectedAddress)
));

Summary

Although JUnit 5 is a great framework and it offers a lot as it goes to assertions, I believe that 3rd party assertion library like AssertJ is needed anyways to spice up assertions a bit. I have been using AssertJ for several years now and I don’t think I will abandon it. But I definitely see a space for new JUnit 5 assertAll in my tests. Especially in integration tests.

All the examples from this blog post (and much more) can be found in this GitHub repository: https://github.com/kolorobot/junit5-samples*

* Big thanks to Maciej Koziara who contributed to this repository.

2 comments:

  1. We share your opinion regarding AssertJ and other assertion frameworks!

    http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions-third-party

    ReplyDelete