25. June 2021
timer-icon 5 min

Kotlin Assertion Libraries – Assertk

Looking for a neat Kotlin assertion library? We got a blog series looking at several Kotlin Assertion Libraries for you! -> assertk

IntroductionAssertJStriktAssertK — KoTest AssertionsAtriumKluentConclusions

Looking at assertk

In this blog post we will be taking a look at version 0.24 of assertk, a library that first appeared back in 2017. It offers, as the name suggests, a largely AssertJ-derived syntax, with several kotlin-specific extensions.

Installation

Installing assertk is a trivial task, all you need is a single dependency that you can find as the first item in assertk’s readme:

Syntax

assertk’s syntax will immediately look familiar as its basic components are directly lifted from AssertJ:


But you don’t need to dig very deep to find some features that take advantage of koltin’s abilities – sometimes for the better, sometimes for the worse. For example the following code will not compile because asserts cannot be made on nullable types straight away:


Nullability issues have to be avoided by explicitly expecting a value to (not) be null:


One might argue that making nullability explicit can make the code – and your intentions behind it – easier to understand. However it is equally true that every other assert you make, like the hasLength(16)in our example, already contains an implicit – and obvious – non-null requirement. So being forced to add an extra isNotNull() adds nothing but needless repetition and noise.

On the other hand asserts acting on all elements of a list look better than in AssertJ as you are no longer forced to repeat the assertThat part:

assertk:


vs

AssertJ:


Assertions can also be grouped, such that every single assertion will be checked for a full report, regardless of early failures. Grouping works for both a single object


and multiple asserts on different objects


One change that might be noticed negatively, or at least will take some time getting used to, is that unlike AssertJ, assertk’s interface is not fluent (with small exceptions like the isNotNull()or isInstanceOf()asserts). For example verifying that an exception has both the correct message and no underlying root cause cannot be done in the style of AssertJ with multiple asserts on the same objects. The code below does not compile because both hasMessage() and hasNoCause return Unit instead of an intermediate Assert object.


To get this test case working again the all grouping mechanism must be used:


One curious feature that assertk offers are so called “table asserts” that allow combining multiple sets of disjoint data for your asserts:


This feature is somewhat reminiscent of JUnit’s parameterised tests, though personally I struggle to come up with a situation where such a feature would not be a code smell. Whatever test ends up working with such a setup is unlikely to be more concise and readable than a few repeated single asserts, or an actual parameterised test.

Error Messages

assertk’s error messages are more detailed than AssertJ’s, as assertk attempts to provide exact information about how the actual result differ from the expectation, instead of just printing both, as AssertJ does. For example in a string comparison that fails because of a typo assertk will point out exactly which letters were different:

Supported Types and Diversity of Assertions

assertk offers a sufficiently versatile set of assertions for the basic String/List/Map/Exception types for you to be able to be productive without many issues. Support for coroutines and and associated types like Flow is available in a separate library.

However outside of these basics the situation is much less ideal. Common java types like Optional and Date have no dedicated support. Same goes for the new java.time classes like OffsetDateTime. These are supported only via general Comparable, so you will need to either write your own asserts or get used to comparing Dates and DateTimes with isGreaterThan and isLessThan instead of isBefore and isAfter (and if you’re anything like me keep reminding yourself that the later Date is the one that’s bigger).

Support for numeric types is likewise only available for the general Number interface. On the one hand that means that there are asserts for all numbers, from Floats, to BigDecimals and AtomicIntegers. On the other hand it means that some useful special cases, like float comparison with a permissible delta, are missing.

All in all a solid foundation, but lacking on the fringes.

Custom Assertions

Creating your own assertions is quite straight forward, and easy to replicate once you’ve seen how it’s done once or twice. New asserts are written as extension functions, for the Assert<T> type, using a given lambda syntax that is reminiscent of BDD descriptions.

There are no special return values to remember, if the assert passes you do nothing, if it doesn’t you just call an expected helper method with a helpful error message.

For example filling assertk’s Optional gap would look like this:

(The show part is a helper method that can be used to wrap or pretty-print values)


Resulting in the following error message:


Making a shorter assert that just delegates to checking some other property or callable is also possible. For example making sure an Optional is empty could be done like this:


and would result in an error message that looks like this:

Active Development

As of the time of writing this blog assertk has a steady stream of contributions and a small number of open issues and PRs. In addition it is supported by WillowTree and has multiple active contributors. Stagnant development is not an issue.

Quality of Documentation

assertk generally offers short and concise, but easily comprehensible javadoc explanations of its features, often supported by simple examples, such that figuring out what assert you need and what it does is simple enough in the vast majority of cases.

Documentation outside the IDE is more sparse, and only really available in the project’s readme on github. However it does a solid job at filling the gaps on topics like installation instructions and custom assertions. The only thing that is really missing is a proper summary and enumeration of all the supported types and available asserts.

IntroductionAssertJStrikt — AssertKKoTest AssertionsAtriumKluentConclusions

Comment article