Concepts

Parameterised Tests

I’ve talked a lot about testing and test driven development in previous blogs. One particularly handy trick I’ve used a few times when testing is parameterised testing using the JUnitParams test library. It’s particularly helpful when you’ve got multiple tests testing broadly the same thing, with some subtle differences – and the cherry on the top is that it’s actually fairly simple to get your head around!

The way parameterised testing works is to allow us to specify parameters for our tests, by adding an @Parameters annotation and passing in a string array of the values we want to pass in. These values are accepted as parameters in the test method and are then substituted in when running the test. Oh and you need to run with the JUnitParamsRunner. That’s it! End of post.

…Actually an example might be useful – and since I’ve used it before I’m going to go with the House Points example (for full requirements see Moving Towards Being Agile – Stories):

Before discovering JUnitParams, one might be inclined to use the following:

public class PointsAdderTest {

    @Test
    public void awardTenPointsToGryffindor() {
        //Given
        int originalPoints = GRYFFINDOR.currentPoints();
        PointsAdder adder = createPointsAdderFor(House.GRYFFINDOR);

        //When
        adder.add(10);

        //Then
        assertThat(GRYFFINDOR.currentPoints()).isEqualTo(originalPoints + 10);
    }

    @Test
    public void awardTenPointsToSlytherin() {
        //Given
        int originalPoints = SLYTHERIN.currentPoints();
        PointsAdder adder = createPointsAdderFor(House.SLYTHERIN);

        //When
        adder.add(10);

        //Then
        assertThat(SLYTHERIN.currentPoints()).isEqualTo(originalPoints + 2 * 10);
    }

    @Test
    public void awardTenPointsToHufflepuff() {
        //Given
        int originalPoints = HUFFLEPUFF.currentPoints();
        PointsAdder adder = createPointsAdderFor(House.HUFFLEPUFF);

        //When
        adder.add(10);

        //Then
        assertThat(HUFFLEPUFF.currentPoints()).isEqualTo(originalPoints + 10);
    }

    @Test
    public void awardTenPointsToRavenclaw() {
        //Given
        int originalPoints = RAVENCLAW.currentPoints();
        PointsAdder adder = createPointsAdderFor(House.RAVENCLAW);

        //When
        adder.add(10);

        //Then
        assertThat(RAVENCLAW.currentPoints()).isEqualTo(originalPoints + 10);
    }
}

This reeks of duplication – the tests are exactly the same, bar the multiple of 10 being added and the house that is being considered! Also, imagine how quickly this test class will grow and become huge – we’ve still got the deductions and edge cases to take into account; it will be very easy to get lost in test code. Now consider this:

@RunWith(JUnitParamsRunner.class
public class PointsAdderTest {

    @Test
    @Parameters({
        "GRYFFINDOR, 1",
        "SLYTHERIN, 2",
        "HUFFLEPUFF, 1",
        "RAVENCLAW, 1"
    })
    public void awardTenPointsTo(House house, int multipleOfPointsToAdd) {
        //Given
        int originalPoints = house.currentPoints();
        PointsAdder adder = createPointsAdderFor(house);

        //When
        adder.add(10);

        //Then
        assertThat(house.currentPoints()).isEqualTo(originalPoints + multipleOfPointsToAdd * 10);
    }
}

Cleaner, smaller, neater, more concise, and easier to see what we’re doing.

A word of caution though – as with methods in your source code, do not start passing in hundreds of parameters to each test – it’s better to have a few different tests rather than one massive convoluted mess of a test where every other word is parameterised and you have to hunt through the String array to figure out what’s going on. Remember, as always, readability is the most important thing when writing code (bar it actually doing what it’s supposed to do, of course).

Advertisements

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