Test data - gravitate towards reuse

by Chris Oxley

A fair percentage of tests I see contain code duplication. Justification for duplication can be to increase understandability and readability. If this is not the case, then duplicate code that constructs test data should be eradicated. This can easily be fixed by abstracting the code into reusable build methods.

I would recommend that you go a step further and put related test data into *TestData objects. *TestData objects can be reused in many tests and if you carefully name the methods it will increase the tests' readability.

Here are a couple of examples:

public class ClubTestData {

    public static IClubBuilder mizunoDriver() {
        return ClubBuilder.aClub().manufacturer(MIZUNO)
                                  .wood().driver().loft(10);
    }

    public static IClubBuilder mizunoThreeIron() {
        return ClubBuilder.aClub().manufacturer(MIZUNO)
                                  .iron().number(3).loft(21);
    }

    public static IClubBuilder mizunoPitchingWedge() {
        return ClubBuilder.aClub().manufacturer(MIZUNO)
                                  .iron().wedge().loft(46);
    }
}

public class GolfBagTestData {

    public static GolfBag mizunoGolfBag() {
        IClub driver = mizunoDriver().build();
        IClub iron3 = mizunoThreeIron().build();
        IClub pitchingWedge = mizunoPitchingWedge().build();
        return new GolfBag(driver, iron3, pitchingWedge);
    }
}

Now, does test data need to be like real data? The example above contains real data. I would advise that, unless the data has meaning or needs to be specific, I would try and keep the data as generic as possible.

Here are a couple of examples of more generalised build methods:

public class ClubTestData {

    private static final BoundedSequence<Manufactorer> 
        manufactorerSequence = aSequenceOfEnumValues(
            Manufactorer.values());

    public static IClubBuilder aClub() {
        int clubNumber = IntegerSequence.nextInt();
        int clubLoft = IntegerSequence.nextInt();
        return ClubBuilder.aClub()
            .manufacturer(manufactorerSequence.next())
            .number(clubNumber).withLoft(clubLoft);
    }
}

public class GolfBagTestData {

    public static GolfBag aGolfBag(int numberOfClubsInBag) {
        IClub[] clubs = new IClub[numberOfClubsInBag];
        for (int i = 0; i < clubs.length; i++) {
            clubs[i] = aClub().build();
        }
        return new GolfBag(clubs);
    }
}

I find that using sequences to generate the data speeds up writing the *TestData objects. I have created the following sequences: BigDecimalSequence, BoundedSequence, DateSequence, IntegerSequence and LongSequence.

Happy testing.