Nicolas A Perez

@anicolaspp

Simplified Testing with ScalaTest and Custom Matchers

Testing first is a great deal for us. We have built upon this principle everything we have, and it definitely works well for us. However, not every testing framework was designed with easiness in mind and sometimes we have to build upon them to get a more refined product that just works better for our special needs for simplicity.

ScalaTest is quite impressive and it has very interesting capabilities. The one we like the most is that we can extend it by adding our own matchers which ultimately gives us the level of abstractions we are looking for.

Let’s start by supposing we want to test a function of the fallowing signature.

A very simplistic test for it could be

As we can see, this is a very simplistic test, but now let’s find a user given an user id.

We could write a test for it as follow.

Wait what? do we actually have to unwrap the monad Option? Well, the other option is the fallowing.

This is kind of the same, the different is that we are now wrapping User into Some which is basically the same.

What we actually want to write is something like the following.

As we can see, in here our intention is way more clear (This code won’t compile, yet)

How can we extend ScalaTest so we get this functionality (or way of writing tests) we want?

This methodology requires a bit of work up front, but it pays back quickly.

First, we need to create a trait that we will mix in with our test classes.

Then, we create the matchTo function we are going to use as part of our test. This function is actually returning a Matcher instance in charge of matching an User and Option[User].

This is what allows us to change

to

In here you should also notice that we are matching User with Option[User] but the comparison is happening behind the scene. We have removed this complexity off our tests.

We could add a new matcher for user so we can write a test with the following shape.

Let’s add the necessary code to UserMatchers.

Now, we can match User by Name and again, under the hood, we are doing all the required comparison to guarantee that the name matches to the user name inside the Option[User].

This example is quite simple, but think about a more complicated stack. Suppose we have a function we want to test that has the following shape.

For this particular function we need to unwrap Future and Either and Seq.

One of the test could be something similar to this.

Wound’t be nice to be able to write the following?

We can write a custom matcher for it, let’s see how.

As you can see, this one is a little more complicated. We have pushed all the dirty stuffs down to the matcher, ValidUsersMatchers. We can argue that it is the same we did before, we just moved the complexity to a different place, but our tests are now cleaner and we can also reuse the matchers for different tests.

Also our tests express in a very clean way our intentions without polluting the code with syntax complexity coming from the stacked constructs such as monads.

Conclusions

Keep your tests clean. ScalaTest has few different ways to do so. Write matchers for you structures so you can reuse them across multiple tests. Mix in as many custom matchers as you need to get as much simplicity as possible. Push the complexity out of your tests to your matchers, let them do the dirty work in one single place.

More by Nicolas A Perez

Topics of interest

More Related Stories