From JUnit to SpringUnit Tests

Removing Duplicate Code

Previous Next
Upon examination of our Brute Force JUnit Test, we can see two things immediately. First, the cases having valid and invalid inputs appear to have different test algorithms. Second, while the inputs and expected results differ between tests, the algorithms for calling the constructor and verifying the outcomes are identical. Therefore, we refactor the test by creating two new methods, runValid and runInvalid, and call these methods, as appropriate, from our testXxx methods. Here are the two new methods.
    protected void runValid(int year, int month, int day,
            int expectedYear, int expectedMonth, int expectedDay) throws Exception {
        CompositeDate subject = new CompositeDate(year, month, day);
        assertTrue(expectedMonth == subject.getMonth());
        assertTrue(expectedDay == subject.getDay());
        assertTrue(expectedYear == subject.getYear());
    }

    protected void runInvalid(int year, int month, int day) throws Exception {
        try {
            new CompositeDate(year, month, day);
            fail("Exception not thrown");
        }
        catch (InvalidDateException ex) {
        }
    }
Here are representative test methods, some that call runValid, and some that call runInvalid.
    public void testJan01() throws Exception {
        int day = 1;
        int month = 1;
        int year = 2006;
        int expectedDay = 1;
        int expectedMonth = 1;
        int expectedYear = 2006;
        runValid(year, month, day, expectedYear, expectedMonth, expectedDay);
    }

    public void testFeb29Leap2004() throws Exception {
        int day = 29;
        int month = 2;
        int year = 2004;
        int expectedDay = 29;
        int expectedMonth = 2;
        int expectedYear = 2004;
        runValid(year, month, day, expectedYear, expectedMonth, expectedDay);
    }

    public void testApr31() throws Exception {
        int day = 31;
        int month = 4;
        int year = 2006;
        runInvalid(year, month, day);
    }

    public void testFeb29NoLeap() throws Exception {
        int day = 29;
        int month = 2;
        int year = 2003;
        runInvalid(year, month, day);
    }
	
Arguably, we could stop here and be satisfied. This version of the test has the benefit of having removed duplicate code, while retaining a benefit that already existed in the first version. Though it may seem so obvious as not worth mentioning, the input and expected data values are contained within, and are therefore lexically close to, each test. This is an important point. The degree to which you value this close lexical association between the test and its data will determine whether you are comfortable performing the next refactoring step (Separating Data from Algorithm), which ultimately leads to a SpringUnit Test.
Previous Next