Assert object pattern

One of the most significant problems with tests written by developers is the fact that such tests do not verify business invariants but the underlying technical details.

When a developer writes a shopping cart part of the application and implements adding an item to the reservation, the test most likely looks like this:

1
2
3
4
5
6
7
8
val reservation = Reservation(...)
val item = Item(...)

reservation.add(item)

reservation.items has size 1
reservation.items.head shouldEqual item
reservation.totalPrice shouldEqual item.price * 1.23

The developer wants to know whether the shopping cart contains the added item, there are no other items in the shopping cart, and whether the VAT tax has been added to the total price of the reservation.

What is wrong with that? This code dissects the objects. I look at the code, and all I see are technical details. Where is the meaning?



Step 1. Hide the assertion

How can we write such tests better? We can use the Assert Object pattern. It is an object that wraps the result and exposes methods which verify the object’s properties. The most trivial usage of this pattern would look like this:

1
2
3
4
5
6
7
8
9
val reservation = Reservation(...)
val item = Item(...)

reservation.add(item)

val assertion = Assert(reservation)
assertion.hasSize(1)
assertion.hasItem(item)
assertion.hasPrice(item.price * 1.23)

It hides the dissecting of the result, but it does not look much better than the previous example.

Step 2. Show the meaning

Fortunately, we can fix that. If we invest some time in implementing the assert objects, we can create a nice fluent interface:

1
2
3
4
5
6
7
8
9
val reservation = Reservation(...)
val item = Item(...)

reservation.add(item)

Assert(reservation)
 .hasItem(item)
 .hasNoOtherItems()
 .hasItemPriceWithVATTax(item)

Why is it better? This is the text we had in the Jira ticket. The text that was written in the specification is written in code. The test shows the user’s intent.

Why don’t we do it?
We must create such assert objects ourselves. We must write the methods of the fluent interface. Assert objects are not given to us for free. It is much easier to import assertions from a testing library and verify the technical details.


Remember to share on social media!
If you like this text, please share it on Facebook/Twitter/LinkedIn/Reddit or other social media.

If you watch programming live streams, check out my YouTube channel.
You can also follow me on Twitter: @mikulskibartosz

If you want to hire me, send me a message on LinkedIn or Twitter.


Bartosz Mikulski
Bartosz Mikulski * big data engineer * conference speaker * co-founder of Software Craftsmanship Poznan & Poznan Scala User Group