Clean JUnit Throwable-Tests with Java 8 Lambdas

Home  >>  JUnit  >>  Clean JUnit Throwable-Tests with Java 8 Lambdas

Clean JUnit Throwable-Tests with Java 8 Lambdas

On July 28, 2014, Posted by , In JUnit, By ,,,, , With 7 Comments

Recently I was involved in a short online discussion on twitter and google+ which concerned the question why the arrival of Java 8 Lambda expressions makes the catch-exception library1 obsolete. This was triggered by a brief announcement that the library won’t be longer maintained as lambdas will make it redundant.

The answer I came up with at that time has a lot in common with the one presented by Rafał Borowiec in his well written post JUNIT: TESTING EXCEPTION WITH JAVA 8 AND LAMBDA EXPRESSIONS. Giving both approaches a second thought however, I believe one could write JUnit throwable-tests even a bit better with respect to clean code.

So this post is a trackback on that topic which shares my latest considerations and explains concisely a slightly refined solution. This way I hopefully will find out about the weak points soon…

 Testing with JUnit

Testing with JUnit Book

Testing with JUnit is one of the most valuable skills a Java developer can learn. No matter what your specific background, whether you’re simply interested in building up a safety net to reduce regressions of your desktop application or in improving your server-side reliability based on robust and reusable components, unit testing is the way to go.

Frank has written a book that gives a profound entry point in the essentials of testing with JUnit and prepares you for test-related daily work challenges.

  Get It Now! 

Motivation

While writing tests I always strive to end up with a clear visual separation of the arrange/act/assert2 phases in a test method (and I am under the impression that it is getting more and more popular to emphasize those phases optically by using empty lines as separator).

Now it seems to me that the catch-exception solutions mentioned above mix the act and assert phases more or less together. This is because both assert that a Throwable has been thrown while still being in the act phase. But an assertion belongs apparently to the assert phase.

See also  JUnit in a Nutshell: Test Structure

Fortunately, this problem can be solved easily.

Refinement

Let’s have look at a simple example to explain how the refined approach might look like. I start with a class that provides a method throwing an IllegalStateException for demonstration purpose:

public class Foo {

  static final String ERR_MESSAGE = "bad";

  public void doIt() throws IllegalStateException {
    throw new IllegalStateException(ERR_MESSAGE);
  }
}

The next snippet introduces a little helper that is responsible for capturing a Throwable thrown during the act phase of a JUnit test. Note that it does not assert anything by itself. It simply returns the captured Throwable if any or null otherwise.

public class ThrowableCaptor {

  public interface Actor {
    void act() throws Throwable;
  }

  public static Throwable captureThrowable( Actor actor ) {
    Throwable result = null;
    try {
      actor.act();
    } catch( Throwable throwable ) {
      result = throwable;
    }
    return result;
  }
}

To highlight that the ThrowableCaptor is used to deal with the act phase of a JUnit Test the captorThrowable method takes a parameter of a type Actor – which admittedly might overdue the metaphor a bit…

Anyway, with that utility in place, AssertJ for clean matcher expressions, static imports and Java 8 lambdas at hand, an exception test might look like this:

public class FooTest {

  @Test
  public void testException() {
    // arrange
    Foo foo = new Foo();
    
    // act
    Throwable actual = captureThrowable( foo::doIt );
    
    // assert
    assertThat( actual )
      .isInstanceOf( IllegalStateException.class )
      .hasMessage( Foo.ERR_MESSAGE );
  }
}

For clarification, I have inserted comments to depict the clear separation of the three phases in the test method. In case that no exception is thrown the assert block would quit this with an assertion error noting that ‘Expecting actual not to be null’3.

See also  Clean Integration Testing with JUnit Rules

Conclusion

By moving the Throwable existence check from the act to the assert phase, the catch-exception approach based on Java8 lambda expressions allows to write such tests in a pretty clean way – at least from my current point of view. ;-)

So what do you think? Am I missing something?


  1. I order to make exception testing cleaner, the catch-exception library catches exceptions in a single line of code and makes them available for further analysis
  2. See Practical Unit Testing, Chapter 3.9. Phases of a Unit Test, Tomek Kaczanowski 2013, often also denoted as build-operate-check pattern, Clean Code, Chapter 9. Unit Tests, Robert C. Martin 2009
  3. The Assertion#isNotNull check is implicitly called by Assertion#isInstanceOf, but it can be called also explicitly of course
Frank Appel
Follow me
Latest posts by Frank Appel (see all)

7 Comments so far:

  1. Rafał says:

    Hello,

    I am an author of the blog post you are referring to (http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html). Thanks!

    In general I agree with your motivation. On the other hand I have one remark.

    I like the AAA and I use it in my tests too. But not always I split act and assert. Such a code:

    // act
    boolean result = sut.doSomething();

    // assert
    assertThat(result).isTrue();

    I would rather to write like below:

    // act & assert
    assertThat(sut.doSomething()).isTrue();

    I think the code is still very readable. And it is more concise. Nothing really wrong with that in my opinion. I think more important is the name of the test method that should be descriptive (e.g. http://blog.codeleak.pl/2013/07/unit-test-names-describe-features.html).

    Of course, it the action requires more code e.g. the method takes some arguments, than probably I would split. It really depends.

    This is more a matter of preference, I would say.

    Anyways, good post!

  2. Lukas Eder says:

    I somehow fail to see the point of these mini-DSLs:

    assertThat( actual )
    .isInstanceOf( IllegalStateException.class )
    .hasMessage( Foo.ERR_MESSAGE );

    The’yre in no way more expressive than writing

    assertTrue( actual instanceof IllegalStateException );
    assertEquals( Foo.ERR_MESSAGE, actual.getMessage() );

    But chances are (very high) that the precise message you’ll eventually be needing is missing – or isn’t named the way you’d expect.

    In fact, I think that with Java 8 and lambdas, most of those internal test-DSLs are obsolete. Once people get used to lambdas, they will be much more idiomatic. I’ve written about that here:

    http://blog.jooq.org/2014/05/23/java-8-friday-better-exceptions/

    An exception to this is Spock, of course.

  3. Thomas Karbe says:

    Very interesting post. However, this works only for methods without parameters so far. A typical use case would be to check for an exception when null is passed to a constructor, which adds two complications to the above scenario:
    a) catching exceptions from methods with parameters
    b) catching exceptions from constructors.

    I am not very familiar with the new lambda syntax, so probably you would come up with a faster solution than me.

    Best Regards
    Thomas

    • Frank Appel says:

      Thomas,
      you would not use the following syntax:

      Throwable actual = captureThrowable( () -> new Foo( null ) );

      Hope this clarifies your point.
      Frank

  4. Rüdiger Herrmann says:

    Just for reference: AssertJ in version 3.x (requires Jre >= 1.8) now also provides a method to capture exceptions:


    Throwable thrown = catchThrowable( foo::doIt );

    assertThat( thrown )
    .isInstanceOf( IllegalStateException.class )
    .hasMessageContaining( Foo.ERR_MESSAGE );