Effective assertions, from Java to Scala

Unit testing should be part of any project. Hard to disagree. However, many complementary tools exist for various purposes. This is the case for assertions, which allow to verify the expected behavior of the system. Different tools have different strengths and weaknesses. Starting within the Java world with JUnit, Hamcrest, FEST-Assert and AssertJ, this high level review will end in the Scala world with ScalaTest and Specs2.

JUnit Assert, the first stop

Junit is the most widely used unit testing library for java. Of course, it comes with its own solution for assertions.

The assertions are static methods following the same signature pattern assertXXX([optionalMessage],expected,actual).

static void assertEquals(java.lang.Object expected,
                         java.lang.Object actual)

// or the same with a message
static void assertEquals(java.lang.String message,
                         java.lang.Object expected,
                         java.lang.Object actual)

A typical example would be :

assertEquals(3,4);
assertEquals("eggs", 3,4);

That’s a good start. Most people will stop here. Indeed, why bother searching for an alternative if the default solution is sufficient? But let’s continue.

Hamcrest, to the rescue?

One issue with JUnit standard assertions is their stiffness. They work perfectly for their exact use case but as soon as a more complex test needs to be written, it is the responsability of the developer to write the additional, boilerplate, code. Hamcrest is a set of matchers created to solve this issue. It is now possible to compose the assertions. JUnit is nowadays bundled with the core of Hamcrest.

The basics are aven simpler : there is a single assert,

static <T> void assertThat(T actual, org.hamcrest.Matcher<T> matcher)

but with powerfull flexibility.

assertThat("Hello", is(not(anyOf(
                                nullValue(),
                                instanceOf(Integer.class),
                                equalTo("Goodbye")))));

Of course, this example is contrieved. But what would be the same with JUnit Assert? More complicated, yes. For those interested, Ed Gibbs wrote a good walkthrough on Hamcrest.

FEST-Assert, the late solution

People may argue that Hamcrest matchers help readability. It may be true with regard to JUnit but not in general. One issue with Hamcrest is that efficient nesting of matchers requires a good knowledge of existing matchers. The Assertions from the FEST project are an alternative solution alleviating this issue by casting the Java type system as a tutor for the developer.

List newEmployees = employees.hired(TODAY);
assertThat(newEmployees).hasSize(6).contains(frodo, sam);

Thanks to the Java type system and IDE autocompletion, the relevant assertions will be shown during the chaining. Learning becomes easier and so does reading. Indeed, the hasSize will only be available for Collection and the chaining removes all the parentheses noise that would have been created by Hamcrest.

Sadly, the project died during its attempt to improve itself with a second version. The latest stable release is from 2011.

AssertJ, the new old

FEST-Assert is dead but… actually was forked and is still alive under a new name : AssertJ. The principle has not changed but additional features have appeared. Among those, the soft assertions should be checked out! In short, even if 10s assertions are written sequentially, on failure, the error message will contain all differences and not only the first one.

org.assertj.core.api.SoftAssertionError:
     The following 4 assertions failed:
     1) [Living Guests] expected:<[7]> but was:<[6]>
     2) [Library] expected:<'[clean]'> but was:<'[messy]'>
     3) [Candlestick] expected:<'[pristine]'> but was:<'[bent]'>
     4) [Professor] expected:<'[well kempt]'> but was:<'[bloodied and disheveled]'>

For new Java projects, I would definitely pick AssertJ over JUnit Assert, Hamcrest or FEST-Assert.

ScalaTest, for Scala

ScalaTest is one of the most widely used test framework for Scala. It “does not try to impose a testing philosophy on you” but supports indeed many ways of expressing a test. Of course, it also includes its own Matchers.

Here are a few examples.

result shouldBe 3  
result should have length 3
result should have size 10

Thanks to a different language, the readability is improved again, even though a few technical artefacts are still left (eg shouldBe). The syntactic sugar allows to remove parentheses, points and semi-colons. Here are the same examples, sugar free.

result.shouldBe(3);
result.should(have(length(3)));
result.should(have(size(10)));

The principle is quite close to how Hamcrest works. The main difference is the root assertion (should) being applied to the observed data instead of being a static method. That’s another Scala feature, ie implicit conversions. The result is transparently converted to another type owning the should or shouldBe methods. It’s the so-called ‘pimp my library’ pattern.

AssertJ fast learning curve is an asset. Should AssertJ used for a project moving from Java to Scala? Well, its lack of native support for Scala types is not in its favor. ScalaTest assertions do not seem to be the most attractive part of Scala for a well advised Java developer.

Specs2?

Specs2, an alternative solution to ScalaTest, has also its own Matchers. But the conclusion is similar. Easier to read? Yes. Easier to learn? Well, maybe not during the first day of Scala.

And you?

Are you a Java developer switching to Scala? Are you a Scala developer coaching Java developer? Have you been on a project migrating from Java to Scala? What are your assertions about assertions?

Bertrand Dechoux

Bertrand Dechoux
Lead Data Engineer/Data Scientist at @Influans. Ex-xebian. Clojure fan for life.

Production readiness : TensorFlow notes

## TensorFlow : a look back at a maturing ecosystemTensorFlow was [open sourced on Novembre 2015](https://research.googleblog.com/2015/11...… Continue reading

Scala code quality with maven

Published on January 27, 2016

A blog in 2016!

Published on January 01, 2016