Generated boilerplate


Nowadays we are pretty used to instruct our powerful IDE to generate lots and lots of boilerplate with just a couple keystrokes. This is specially the case for Java and, as a consequence, we become as blind to generated code as we used to be blind to banners (until we discovered AdBlock).

Before and after AdBlock

Before and after AdBlock (via Lancelhoff)

It is most likely that the junit test template generated by your IDE will look like the following snippet.

import org.junit.Test;

public class StatisticsCollectorTest {

    @Test
    public void testCollectStatistics() throws Exception {
    }
}

Just for today, let’s focus on the generated code. Notice the throws Exception part and think why it is there before continuing reading.

That declaration is not there to save you from typing another fancy keystroke to add a throws clause, which is not so helpful anyway. The throws declaration is part of a good practice that helps your test cases to be more independent of each other.

Consider the example sketched in the snippet: you have a class collecting some sort of statistic about a collection of elements. After some TDD, you have the following tests:

import org.junit.Test;

public class StatisticsCollectorTest {

    @Test
    public void shouldBeZeroForEmptyInput() throws Exception { ... }

    @Test
    public void shouldTakeTheAverageFoo() throws Exception { ... }

    @Test
    public void shouldFilterDisabledElements() throws Exception { ... }

    @Test
    public void shouldBeNaNIfAnyElementIsTooBig() throws Exception { ... }
}

Notice that the special value NaN is expected in the case of an anomalous input. We might want to throw a checked exception instead of just a NaN, and nothing stops us from doing so without modifying other tests thanks to the throws declarations.

@Test(expected = TooBigElement.class)
public void shouldThrowIfAnyElementIsTooBig() throws Exception { ... }

Without that throws Exception, this change could cascade not only to the other cases in this test but to many other test classes in what is called shotgun surgery. The need to modify several tests when making a single conceptual change is a signal of poor quality tests that are not independent enough. You can thank your IDE for this one.

For the production code, this change might be costly as you need to modify all the signatures between this function and the code handling the exception. As Michael Feathers points it out in a classic book1, checked exceptions are not worthy for the general case because they violate the OCP principle.