Refactoring Tests (or: How I Learned to Stop Worrying and Love the Red Bar)

When code is refactored, the tests associated with it help you to be sure that you haven’t broken anything.

But code doesn’t test the tests! If you’ve ever renamed a test and accidentally typed tsetThat... at the beginning, you have already experienced this.

So, here’s what I do to make refactoring tests safer. First of all I work out what element of behaviour my test defines:

public void testThatWhenSaleIsMadeReceiptIsPrintedWithAppropriateAmount() {

    LoginScreen loginScreen = new LoginScreen();
    loginScreen.enterLogin("Liz");
    loginScreen.enterPassword("Pa55word");
    loginScreen.clickOk();
    SaleScreen saleScreen = new SaleScreen();
    ...
    assertEquals(10.00, receiptPrintOut.getPrice());

}

and I break that behaviour by changing the code, maybe by adding an exception:

public class Receipt {

    ...

    public void print() {
        ...
        /** @todo remove this exception */
        if (price == 10.00) { throw new Exception("Remove This"); }
       ...
    }

    ...

}

then I refactor the test against the red bar!

public void testThatWhenSaleIsMadeReceiptIsPrintedWithAppropriateAmount() {

    new LoginFixture().login();
    ReceiptPrintOut receiptPrintOut = new SaleFixture().buyItem(9123456);
    assertEquals(10.00, receiptPrintOut.getPrice());

}

As long as the test is failing because of my broken code (and not because of some null pointer which means that I’ve broken the test), it’s likely that it’s testing the right thing. When I remove the exception in the code, and see my test run green again, I have the same certainty that my test is still behaving itself as I do when I’m refactoring code. I can remove the exception at any time to check that my test still works.

For me, this technique has been particularly useful for those interminably long acceptance tests which have lots of duplicated functionality, or any complex change that my IDE couldn’t handle automatically. It’s also good for introducing mocks and stubs to tests; eg: introducing a stubbed data source instead of a real database, a custom output stream instead of a file, etc., as I know I won’t accidentally miss out the thing I was testing in the first place. I’ve also used it for refactoring an Ant build, to check that the patternset and classpaths I was using were still finding the tests.

It doesn’t always work, especially since acceptance tests frequently check more than one thing, but it helps.

This entry was posted in Uncategorized. Bookmark the permalink.

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 )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s