AssertJ, Stands On Its Own

Smell The Clutter

Java, with a healthy dose of testing code, has been my mainstay off and on for many years now.  JUnit and assertions have been a big part of my goto tools for most of that.  JUnit’s assertions felt underpowered from the start so, like most folks, I jumped on frameworks like hamcrest and fest. In some of code that’s old, or had a lot of hands in it, the result is a bit of a confused mess of all the possible frameworks.  Messy tests is as bad a code smell as any. Hamcrest hasn’t seen changes in ages and it appears fest is now abandon-ware so I decided it was time to deal with the smell.

Fest had been my preferred assertion framework and so AssertJ, the fest descendant with a still active community, was an obvious choice.  Some initial test usages proved it to be a cleaner fest. And so I began refactoring tests to use only AssertJ, removing JUnit’s, fest’s and hamcrest’s assertions.

Using AssertJ

AssertJ has a nice clean approach to assertions. It uses a factory method Assertions.assertThat() to create a type specific assertion. The type specific assertions offer fluent interfaces that are largely polymorphic.

Simple Example

static import org.assertj.core.api.Assertions.*;
...
@Test
public void shouldProvideAnExample() {
    String actual = "This is a test";
    assertThat(actual).contains("is");
    String[] actualArray = new String[]
               {"This", "is", "a", "test"};
    assertThat(actualArray).contains("is");
}

In the example above, assertThat() creates the appropriate type of assertion (String, Array) and contains, on a string looks in the string, and on the array searches the array.

Fluent API

One of AssertJ’s feature I like is its fluent API. In particular it’s cleaner than hamcrest’s approach IMHO. Consider, for example, determining if a string is between two values. There is a isBetween, but even barring that, the fluent approach looks like:

     assertThat(actual).isGreaterThan("bbb")
                         .isLessThan("ccc");

Where as hamcrest this would read (if there was greaterThan and lessThan):

     assertThat(actual, 
                 allOf(greaterThan("bbb"), 
                       lessThan("ccc")));

Clearly the two approaches are equally expressive, but I find the fluent syntax easier to scan.

Custom Conditions

import org.assertj.assertions.core.Condition;
static import org.assertj.core.api.Assertions.*;
...
@Test 
public void shouldBeEvenlyDivisibleBySix() { 
    Condition<Integer> evenDivBySix = new Condition<Integer>() { 
        @Override 
        public boolean matches(Integer value) { 
            return (value % 6) == 0; 
        } 
    }; 
    assertThat(12).is(evenDivBySix); 
    assertThat(8).isNot(evenDivBySix); 
}

In this example a condition was created that determined if an integer is evenly divisible by six and then used in assertions on integers.

Custom Assertions

Sometimes you’ve your own types with corresponding common assertions you’d like to apply to them. Perhaps you want to know if a Student instance isInMiddleSchool?
I won’t walk through all the interface implementations here, but the process is quite straight forward.

  1. Subclass AbstractAssert for Student and implement a isInMiddleSchool.
  2. Subclass Assertions adding a new assertThat factory method for your StudentAssert you created.
  3. Use your new factory and assertions as you’d always (i.e. assertThat(student).isInMiddleSchool())

That simple.

The Refactoring

The transition from fest was basically changing some imports and a few small tweaks (see Assertj Migrating from Fest).  JUnit’s assertions were largely an import change and a simple regex or two. Hamcrest was a bit uglier…. and more satisfying.

What Didn’t Work Out?

Everything worked out except a few glitches around Hamcrest.

  • Hamcrest has native bean support via getProperty that AssertJ doesn’t offer. Coded around those using AssertJ’s Conditions.  Probably could have found another Bean/POJO accessor class but was trying to reduce frameworks.
  • At least one mocking framework depends on Hamcrest for some features.  The dependence was minimal and didn’t introduce much clutter into the tests.

Slides Based on This Post

Advertisements

6 thoughts on “AssertJ, Stands On Its Own

  1. Hi,

    I’m seriously thinking in migrate from Hamcrest to AssertJ but I’m little bit afraid about yours last comments :-).

    – Exactly, what is the problem of Assert does not support native bean support ?
    – Which mocking framework depends on Hamcrest? I hope It won’t be Mockito :-S

    Thanks!

    • On the bean support, for me it was not an issue. Never would have thought to do it. It was only in dealing with someone else’s code. And besides there are a couple of open source comparators out there if you really want that.

      Yeah I think it’s Mockito, but it’s not an issue, is in some pretty specific situations.

    • Wasn’t meant as a complaint, it was just a snag in porting from another kit. I’m a convert 100% to AssertJ (Thank you for it!).

      I’ll dig out an example and post it, but it was basically a matcher that used reflection to treat any object like a bean, allowing comparisons by name. I has happier changing the approach, it seemed a sort of sloppy way to do things.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s