AssertJ test cause of exception

Posted at — May 8, 2020
Riekpil logo
Learn how to test real-world applications with the Testing Spring Boot Applications Masterclass. Comprehensive online course with 8 modules and 130+ video lessons to master well-known Java testing libraries: JUnit 5, Mockito, Testcontainers, WireMock, Awaitility, Selenium, LocalStack, Selenide, and Spring's Outstanding Test Support.

AssertJ 3.16.0 has just been released. It has a feature I contributed to allow testing the (root) cause of an exception.

This blog post will show how to use this to our advantage.

Let’s get started with a simple Spring Boot application using Spring Data JPA.

We will have 3 entities: Book, Song and User

A User can have exactly 1 favorite book and 1 favorite song. The book and the song have to be in the database already before the user can make it a favorite of his.

Check the sources on Github for the full code.

This is how the User looks like:

@Entity
public class User {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Song favoriteSong;

    @ManyToOne(fetch = FetchType.LAZY)
    private Book favoriteBook;

    ...
}

The code also has BookRepository, SongRepository and UserRepository.

The test we will focus on is the UserRepositoryTest:

@Test
void testUnableToSaveUserIfBookNotInDatabase() {
    Book book = new Book();
    book.setTitle("AssertJ in action");

    User user = new User();
    user.setFavoriteBook(book);

    assertThatExceptionOfType(IllegalStateException.class)
            .isThrownBy(() -> {
                repository.save(user);
                entityManager.flush();
            })
            .havingCause()
            .withMessageMatching(".*object references an unsaved transient instance.*User.favoriteBook.*");
}

What we test here is that if a User has a favorite Book that is not yet saved in the database, we get an IllegalStateException. Not only that, but we also check that there is a cause (via havingCause()) and that this cause should have a particular message where the User.favoriteBook field is present.

A similar test can be done if a Song is not yet saved in the database:

 @Test
void testUnableToSaveUserIfSongNotInDatabase() {
    Song song = new Song();
    song.setTitle("Bee Gees - Stayin' Inside");
    User user = new User();
    user.setFavoriteSong(song);
    assertThatExceptionOfType(IllegalStateException.class)
            .isThrownBy(() -> {
                repository.save(user);
                entityManager.flush();
            })
            .havingCause()
            .withMessageMatching(".*object references an unsaved transient instance.*User.favoriteSong.*");
}

See Checking cause and root cause in the AssertJ documentation for more information about this handy function.

If you want to be notified in the future about new articles, as well as other interesting things I'm working on, join my mailing list!
I send emails quite infrequently, and will never share your email address with anyone else.