JUnit in a Nutshell: Test Structure

Home  >>  JUnit  >>  JUnit in a Nutshell: Test Structure

JUnit in a Nutshell: Test Structure

On August 18, 2014, Posted by , In JUnit, By ,,, , With 4 Comments

Chapter two of my multi-part tutorial about JUnit testing essentials will continue the ongoing example and work out the common structure that charactarizes well written unit tests. The nomenclature used throughout this post was defined by Meszaros in xUnit Test Patterns [MES].

The Four Phases of a Test

A tidy house, a tidy mindOld Adage

The tutorial’s example is about writing a simple number range counter, which delivers a certain amount of consecutive integers, starting from a given value. Beginning with the happy path the last post’s outcome was a test which verified, that the NumberRangeCounter returns consecutive numbers on subsequent invocations of the method next:

  @Test
  public void subsequentNumber() {    
    NumberRangeCounter counter = new NumberRangeCounter();

    int first = counter.next();
    int second = counter.next();

    assertEquals( first + 1, second );
  }
Note that I stick with the JUnit build-in functionality for verification in this chapter. I will cover the pro and cons of particular matcher libraries (Hamcrest, AssertJ) in a separate post.

The attentive reader may have noticed that I use empty lines to separate the test into distinct segments and probably wonders why. To answer this question let us look at each of the three sections more closely:

1 The first one creates an instance of the object to be tested, referred to as SUT (System Under Test). In general this section establishes the SUT’s state prior any test related activities. As this state constitutes a well defined test input, it is also denoted as fixture of a test.

2 After the fixture has been established it is about time to invoke those methods of the SUT, which represent a certain behavior the test intends to verify. Often this is just a single method and the outcome is stored in local variables.

3 The last section of the test is responsible to verify whether the expected outcome of a given behavior has been obtained. Although there is a school of thought propagating a one-assert-per-test policy, I prefer the single-concept-per-test idea, which means that this section is not limited to just one assertion as it happen to be in the example [MAR1].

This test structure is very common and have been described by various authors. It has been labelled as arrange, act, assert [KAC] – or build, operate, check [MAR2] – pattern. But for this tutorial I like to be precise and stick with Meszaros’ [MES] four phases called setup (1), exercise (2), verify (3) and teardown (4).

4 The teardown phase is about cleaning up the fixture in case it is persistent. Persistent means the fixture or part of it would survive the end of a test and may have bad influence on the results of its successor.

Plain unit tests seldomly use persistent fixtures so the teardown phase is – as in our example – often omitted. And as it is completely irrelevant from the specification angle, we like to keep it out of the test method anyway. How this can be achieved is covered in a minute.

Due to the scope of this post I avoid a precise definition of a unit test. But I hold on to the three types of developers’ tests Tomek Kaczanowski describes in Practical Unit Testing with JUnit and Mockito and can be summarized to:

Unit tests make sure that your code works and have to run often and therefore incredibly quickly. Which is basically what this tutorial is all about.

Integration tests focus on the proper integration of different modules, including code over which developers have no control. This usually requires some resources (e.g. database, filesystem) and because of this the tests run more slowly.

End-to-End tests verify that your code works from the client’s point of view and put the system as a whole to the test, mimicking the way the user would use it. They usually require a signification amount of time to execute themselves.

And for an in-depth example of how to combine these testing types effectively you might have a look at Growing Object-Oriented Software, Guided by Tests by Steve Freeman and Nat Pryce.

But before we go ahead with the example there is one question left to be discussed:

Why is this Important?

The ratio of time spent reading (code) versus writing is well over 10 to 1…Robert C. Martin, Clean Code

The purpose of the four phases pattern is to make it easy to understand what behavior a test is verifying. Setup always defines the test’s precondition, exercise actually invokes the behavior under test, verify specifies the expected outcome and teardown is all about housekeeping, as Meszaros puts it.

This clean phase separation signals the intention of a single test clearly and increases readability. The approach implies that a test verifies only one behavior for a given input state at a time and therefore usually does without conditional blocks or the like (Single-Condition Test).

While it is tempting to avoid tedious fixture setup and test as much functionality as possible within a single method, this usually leads to some kind of obfuscation by nature. So always remember: A test, if not written with care, can be a pain in the ass regarding maintenance and progression.

See also  JUnit in a Nutshell: Unit Test Assertion

But now it is time to proceed with the example and see what this new knowledge can do for us ;-)

Corner Case Tests

Once we are done with the happy path test(s) we continue by specifying the corner case behavior. The description of the number range counter states that the sequence of numbers should start from a given value. Which is important as it defines the lower bound (one corner…) of a counter’s range.

It seems reasonable that this value is passed as configuration parameter to the NumberRangeCounter‘s constructor. An appropriate test could verify that the first number returned by next is equal to this initialization:

  @Test
  public void lowerBound() {
    NumberRangeCounter counter = new NumberRangeCounter( 1000 );

    int actual = counter.next();
    
    assertEquals( 1000, actual );
  }

Once again our test class does not compile. Fixing this by introducing a lowerBound parameter to the counter’s constructor, leads to an compile error in the subsequentNumber test. Luckily the latter test has been written to be independent from the lower bound definition, so the parameter can be used by the fixture of this test, too.

However the literal number in the test is redundant and does not indicate its purpose clearly. The latter is usually denoted as magic number. To improve the situation we could introduce a constant LOWER_BOUND and replace all literal values. Here is what the test class would look like afterwards:

public class NumberRangeCounterTest {
  
  private static final int LOWER_BOUND = 1000;

  @Test
  public void subsequentNumber() {
    NumberRangeCounter counter = new NumberRangeCounter( LOWER_BOUND );
    
    int first = counter.next();
    int second = counter.next();
    
    assertEquals( first + 1, second );
  }
  
  @Test
  public void lowerBound() {
    NumberRangeCounter counter = new NumberRangeCounter( LOWER_BOUND );

    int actual = counter.next();
    
    assertEquals( LOWER_BOUND, actual );
  }
}

Looking at the code one may notice that the fixture’s in-line setup is the same for both tests. Usually an in-line setup is composed of more than a single statement, but there are often commonalities between the tests. To avoid redundancy the things in common can be delegated to a setup method:

public class NumberRangeCounterTest {
  
  private static final int LOWER_BOUND = 1000;

  @Test
  public void subsequentNumber() {
    NumberRangeCounter counter = setUp();
    
    int first = counter.next();
    int second = counter.next();
    
    assertEquals( first + 1, second );
  }
  
  @Test
  public void lowerBound() {
    NumberRangeCounter counter = setUp();

    int actual = counter.next();
    
    assertEquals( LOWER_BOUND, actual );
  }
  
  private NumberRangeCounter setUp() {
    return new NumberRangeCounter( LOWER_BOUND );
  }
}

While it is debatable if the delegate setup approach improves readability for the given case, it leads to an interesting feature of JUnit: the possibility to execute a common test setup implicitly. This can be achieved with the annotation @Before applied to a public, non static method that does without return value and parameters.

Which means this feature comes to a price. If we want to eliminate the redundant setUp calls within the tests we have to introduce a field that takes the instance of our NumberRangeCounter:

public class NumberRangeCounterTest {
  
  private static final int LOWER_BOUND = 1000;
  
  private NumberRangeCounter counter;
  
  @Before
  public void setUp() {
    counter = new NumberRangeCounter( LOWER_BOUND );
  }

  @Test
  public void subsequentNumber() {
    int first = counter.next();
    int second = counter.next();
    
    assertEquals( first + 1, second );
  }
  
  @Test
  public void lowerBound() {
    int actual = counter.next();
    
    assertEquals( LOWER_BOUND, actual );
  }
}

It is easy to see that implicit setup can remove a lot of code duplication. But it also introduces a kind of magic from the view point of a test, which can make it difficult to read. So the clear answer to the question ‘Which kind of setup type should I use?’ is: it depends…

As I usually pay attention to keep units/tests small, the trade off seems acceptable. So I often use the implicit setup to define the common/happy path input and supplement it accordingly by small in-line/delegate setup for each of the corner case tests. Otherwise as in particular beginners tend to let tests grow too large, it might be better to stick with in-line and delegate setup first.

The JUnit runtime ensures that each test gets invoked on a new instance of the test’s class. This means the constructor only fixture in our example could omit the setUp method completely. Assignment of the counter field with a fresh fixture could be done implicitly:

private NumberRangeCounter counter = new NumberRangeCounter( LOWER_BOUND );

While some people use this a lot, other people argue that a @Before annotated method makes the intention more explicit. Well, I would not go on war over this and leave the decision to your personal taste…

Implicit Teardown

Imagine for a moment that NumberRangeCounter needs to be disposed of for whatever reason. Which means we have to append a teardown phase to our tests. Based on our latest snippet this would be easy with JUnit, as it supports implicit teardown using the @After annotation. We would only have to add the following method:

  @After
  public void tearDown() {
    counter.dispose();
  }

As mentioned above teardown is all about housekeeping and adds no information at all to a particular test. Because of this it is very often convenient to perform this implicitly. Alternatively one would have to handle this with a try-finally construct to ensure that teardown is executed, even if a test fails. But the latter usually does not improve readability.

See also  Lightweight Integration Tests for Eclipse Extensions

 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! 

Expected Exceptions

A particular corner case is testing expected exceptions. Consider for the sake of the example that NumberRangeCalculator should throw an IllegalStateException if a call of next exceeds the amount of values for a given range. Again it might be reasonable to configure the range via a constructor parameter. Using a try-catch construct we could write:

  @Test
  public void exeedsRange() {
    NumberRangeCounter counter = new NumberRangeCounter( LOWER_BOUND, 0 );

    try {
      counter.next();
      fail();
    } catch( IllegalStateException expected ) {
    }
  }

Well, this looks somewhat ugly as it blurs the separation of the test phases and is not very readable. But since Assert.fail() throws an AssertionError it ensures that the test fails if no exception is thrown. And the catch block ensures that the test completes successfully in case the expected exception is thrown.

With Java 8 it is possible to write cleanly structured exception tests using lambda expressions. For more information please refer to Clean JUnit Throwable-Tests with Java 8 Lambdas.

If it is enough to verify that a certain type of exception has been thrown, JUnit offers implicit verification via the expected method of the @Test annotation. The test above could then be written as:

  
  @Test( expected = IllegalStateException.class )
  public void exeedsRange() {
    new NumberRangeCounter( LOWER_BOUND, ZERO_RANGE ).next();
  }

While this approach is very compact it also can be dangerous. This is because it does not distinct whether the given exception was thrown during the setup or the exercise phase of a test. So the test would be green – and hence worthless – if accidently an IllegalStateException would be thrown by the constructor.

JUnit offers a third possibility for testing expected exceptions more cleanly, the ExpectedException rule. As we have not covered Rules yet and the approach twists a bit the four phase structure, I postpone the explicit discussion of this topic to a follow-up post about rules and runners and provide only a snippet as teaser:

public class NumberRangeCounterTest {
  
  private static final int LOWER_BOUND = 1000; 

  @Rule
  public ExpectedException thrown = ExpectedException.none();

  @Test
  public void exeedsRange() {
    thrown.expect( IllegalStateException.class );
   
    new NumberRangeCounter( LOWER_BOUND, 0 ).next();
  }

  [...]
}

However if you do not want to wait you might have a look at Rafał Borowiec‘s thorough explanations in his post JUNIT EXPECTEDEXCEPTION RULE: BEYOND BASICS

Test Structure Conclusion

This chapter of JUnit in a Nutshell explained the four phase structure commonly used to write unit tests – setup, exercise, verify and teardown. It described the purpose of each phase and emphasized on how it improves readability of test cases when consistently used. The example deepened this learning material in the context of corner case tests. It was hopefully well-balanced enough to provide a comprehensible introduction without being trivial. Suggestions for improvements are of course highly appreciated.

The next chapter of the tutorial will continue the example and cover how to deal with unit dependencies and test isolation, so stay tuned.

Chapter Navigation
 Prev | Table of Content | Next 

References

[MES] xUnit Test Patterns, Chapter 19, Four-Phase Test, Gerard Meszaros, 2007
[MAR1] Clean Code, Chapter 9: Unit Tests, page 130 et seqq, Robert C. Martin, 2009
[KAC] Practical Unit Testing with JUnit and Mockito, 3.9. Phases of a Unit Test, Tomek Kaczanowski, 2013
[MAR2] Clean Code, Chapter 9: Unit Tests, page 127, Robert C. Martin, 2009

Title Image: © Depositphotos.com/leszekglasner
Frank Appel
Follow me
Latest posts by Frank Appel (see all)

4 Comments so far:

  1. […] tutorial from Frank Appel covers the fundamentals of test driven development on JUnit with proper test structure, test isolation, and test runners. Learn how to get up and running with plenty of examples and best […]

  2. Weeber says:

    How does the last:
    @Test
    public void exeedsRange() {
    thrown.expect( IllegalStateException.class );
    new NumberRangeCounter( LOWER_BOUND, 0 ).next();
    }

    Differs from:
    @Test( expected = IllegalStateException.class )
    public void exeedsRange() {
    new NumberRangeCounter( LOWER_BOUND, ZERO_RANGE ).next();
    }

    To me it still seems like it confuses where the exception will be thrown since the instantiation and method execution are still on the same line… I’m obviously missing something (I’m new to junit) could you please explain it to me?

    • Frank Appel says:

      Hi, you’re completely right. The exception rule approach is far from being a perfect solution. The given example can run in the same problem as the one mentioned with the ‘expected’ annotation attribute. Sorry, if I conveyed a different impression.

      The point why it is often a better solution is because it offers more fine-grained verification possibilities. It allows, for example, to check for exception message content.

      With respect to test structure and fail-safety, the try-catch example is the way to go prior to Java 8. As mentioned in the infobox, it is possible to use lambda expressions to get a more clean test structure (see: /2014/07/28/clean-junit-throwable-tests-with-java-8-lambdas/)