26. March 2021
timer-icon 6 min

Kotlin Assertion Libraries - AssertJ as Baseline

Looking for a neat Kotlin assertion library? We got a blog series looking at several Kotlin assertion libraries for you! -> AssertJ
kotlin assertion assertj

IntroductionAssertJStriktAssertKKoTest AssertionsAtriumKluentConclusions

Looking at AssertJ in Kotlin

AssertJ will serve as a baseline we will measure all other Kotlin assertion libraries against. So, as a start, we will have a look at how it is set up, how it is used, and point out its shortcomings when used with Kotlin that would make using another library worthwhile in the first place.

Having started out as a fork of something called fest-assert all the way back in 2010 AssertJ is nowadays without a doubt the king of assertion libraries on the JVM. It is easy to install and get going, its fluent interface offers the most simple and expressive syntax possible in a language like Java and it supports a large variety of use-cases, from the mandatory basics like Strings and Lists to Arrays of shorts and bytes to modules for Guava, Joda, and Neo4j.

Installation

Installing AssertJ is a simple as adding a 3rd party dependency can possibly be. Head over to its documentation (the first result of a google search), find the point that says Quick Start and Ctrl-c Ctrl-v the code for your build system.

For example for Gradle all you need is


For Maven it is


That is it, your are now ready to use AssertJ.

Syntax and Error messages

The most common entry point for AssertJ is Assertions.assertThat():


AssertJ offers a fluent interface, so it is possible to assert multiple things at the same time without the need for repeated assertThat:


There are several assertions that are able to profit from Kotlins improved lambda syntax. For example asserting that all/some/no items of a list satisfy some predicate (specifically it is a Consumer<T>, hence the need for having to nest the assertThatcalls):


Error messages follow a readable top-down format. For example a mismatch for the content of a list would look like this:


Error messages and descriptions can even be fully customized to help explain just what exactly is being asserted and why.

(There is a minor caveat in that the overrides must be applied before the assert, otherwise they will have no effect.)


Such error messages will also override the default reporting and can therefore hide details that would otherwise be there. For our case of a list containing only 3 items, thus failing the second condition, this setup will result in the following output:

Supported Types and Diversity of Assertions

At this point we are almost tempted to say that it would easier to make a list of things AssertJ does not offer first class support for. Almost, as actually finding them would require an extensive search for little gain – AssertJ offers specialized assertions for

  • Basics language building blocks like Objects, Strings, Lists, Maps and Arrays
  • Primitives like ints and bytes and Arrays of Primitives
  • Dates
  • Exceptions
  • Java 8 novelties like Streams, Optionals and the new temporal types like OffsetDateTime
  • Relatively less common types like BigDecimal and Class<?>
  • Files and Paths
  • Futures and CompletableFutures
  • Atomic types like AtomicInteger
  • Support for database asserts with assertj-db
  • Guava types with assertj-guava
  • Joda types with assertj-joda
  • Neo4j types with assertj-neo4j
  • Due to extendability several separate extension Libraries exist (e.g. Webtester2 AssertJ-Support)

The same can be said for the diversity of asserts that can be tested: from the obvious is(Not)Null and isEqualTo checks you will usually be able to find whatever you need, like

  • whitespace, substring and case-insensitive asserts for strings
  • positive and negative predicates and order-sensitive and -insensitive asserts for lists and maps’ keys, values and entries
  • is-before, -after and -between asserts for dates
  • close-enough equality for Floats, Doubles and BigDecimals

Custom Assertions

And if you do find a gap in AssertJ’s repertoire it is easy enough to fill it yourself: Suppose we have a Product business class, and we would like to have custom assertions for its state. AssertJ is based on Java, so we will use somewhat “oldschool” tools to make it happen: a custom class with inheritance, a factory method and self-referential generics:


The generics part looks slightly complicated at first. It’s a combination of the types of the assert class, the type being asserted on, as well as their instances. This shouldn’t be a problem to remember after doing it once or twice. With that out of the way we also need a factory method to use our new assertions with the same assertThat(product) entry-point as AssertJ’s own:


Now we are ready to use our new asserts:


So far so good, but now it gets complicated: what happens if you bring inheritance into the mix? Let’s think back to one of those “Introduction to Object Oriented Programming” tutorials that we all have probably read at one point and use something like that as our starting point – we’ll have a Person base class and an Employee sub class. The former has a name, the latter also a job title:


What shall we write to make the following fluent code possible?


For a case like that you can find a quick example here. It uses the AssertJ api and extends it using inheritance.


This code serves as an example that demonstrates that, while AssertJ may be the king of Java assertions, it is not entirely free of such spooky corners.

For reference a guide on how to write your own assertions can be found in AssertJ’s legacy doumentation, though it does not cover the more complex case like we described above.

Active Development

Although AssertJ has reached a high degree of maturity it is still actively maintained and improved. As of the time of writing this blog post github’s Insights show that not a single week has passed in the last year without multiple commits being made. And even if that were not the case, AssertJ is a mature library that does not really need many more new features and could still be considered a solid choice for most projects even if its development had slowed down to a crawl.

Quality of Documentation

AssertJ’s web site contains a very thorough documentation for all its features with their nooks and crannies and knobs you can turn and levers you can pull. You just need to search for the right keyword, or find the right headline. The javadoc is all around solid as well, always providing both a functional description of what an assertion is for, as well as examples on how to use it. If we try really hard we can point to methods’ type signatures as being a downside – for example the check that an Optional is present operates on an AbstractOptionalAssert<SELF extends AbstractOptionalAssert<SELF, VALUE>, VALUE> – but from personal experience we cannot remember even a single instance that this has ever been a problem.

So What Now?

So much for the (many) strengths and (few) weaknesses of AssertJ. Now the question is this: if AssertJ is already such an all around solid choice why not just keep using it for our Kotlin assertion usecases? The answer is not much different than the reasons we would give for wanting to use Kotlin over Java in the first place. Because it allows us to do more with less, because its syntax is more concise and simultaneously more expressive, because its type system can eliminate entire categories of errors. So we set out on a search for a library that can realize these promises.

IntroductionAssertJStriktAssertKKoTest AssertionsAtriumKluentConclusions

Comment article