Effective Testing - Expressive Assertions
Using expressive assertions can help us figure out why a test fails without having to go through the code.
Let’s start with an example. Here’s a test making sure our recipe has tomatoes 🍅
At first glance, everything looks ok. The test passes, it is easy to read, and it follows the ”Given - When - Then” structure.
Some months go by, and one day our test starts failing.
At this point, we’ve probably forgotten everything about the recipe, and we’re not sure what’s causing the failure. The test name is not helping us much either.
To avoid this situation, we can include a message that will be displayed whenever the assertion fails.
Now the failure is obvious. There is no 🍅 on the recipe. We can immediately tell what’s wrong without even looking at the test code. But we can do better…
Let’s face it, writing this kind of detailed message for every assertion would be a pain in the ass. Fortunately, we don’t have to. Instead, we can use an expressive assertion library to do the heavy lifting for us. This is how our code would look like using Strikt:
We’re using Kotlin infix notation to make the code more readable. This a stylistic decision, you don’t have to use it if you don’t like it.
You might notice we’re calling the
contains method on the assertion itself. This is possible because Strikt can tell that the type we’re asserting on, is a String, and thus, it can provide methods explicitly tailored to Strings. This is what the error message would look like in this case:
Almost the same information we got from writing our own message, but without the boilerplate.
Assertion libraries are like swiss army knives; they provide all kinds of assertions for different types of objects. I suggest learning a few of the core ones through the documentation, and then letting the IDE guide you with auto-suggestions to discover new ones.
Here are some more examples of type-specific assertions:
Asserting on objects
When validating properties on objects, you might be tempted to write something like this:
You can probably tell why this is bad. By the time we see the failure, we no longer have context on what property we’re asserting.
Was it checking the
author, or something else?
Instead, you can take advantage of the fact that Data Classes automatically get
toString implementations. So we can use an
assertEquals and get a nice looking message showing us both instances.
If we don’t care about comparing all properties we can use Strikt to assert only specific fields:
The block assertion style means that even though the title assertion failed Strikt will still check for page count and it’ll provide output for all the assertions in the block.
This post is part of the Effective Testing Series.