Adding tests to legacy applications

I came across this great talk from Jason Swett today; it’s short and quite fun, but really good.

Some key takeaways were

Test the easy things first

Things like the checkout process on a shopping app might be the most valuable places to have tests, but are also likely to be amongst the hardest to test. When you’re adding tests to an application that already exists, there’s a lot of groundwork to do in creating infrastructure just to get the tests running in the first place, so aim to make the first test you write as easy as possible – aim for something like the login flow which is likely to be much easier to test.

Use Sprout Method/Sprout Class approaches

When you have code written without tests, you’ll tend to get quite long, knotty, interrelated methods which know far too much about the rest of the system and which have a lot of dependencies. You’re also going to struggle to know what a method does in many cases. In order to break this up in a sane way, as recommended by Michael Feathers in Working Effectively with Legacy Code and discussed in Refactoring by Martin Fowler, you can use the Sprout Method and Sprout Class approaches. Here, you can work through a complicated method line by line, extracting each line or clump of related lines that do one thing into their own method – maybe even their own class. Extracted in this way each method will have only one or two dependencies at most and therefore becomes possible, sometimes easy, to test.

Characterisation tests

Obviously it’s hard to write a test when you don’t know what the code returns, so writing characterisation tests is a useful technique. Your mindset is a bit different in this phase – you’re not necessarily thinking critically about whether it’s doing the right things, but like a stenographer you’re simply recording what the code does.

Jason will comment out an entire method body and then bring it back line by line, and for each line write a failing test that then passes when the line is uncommented. Doing this when you don’t know what it does is still possible – just write a test asserting something you know is wrong, like asserting a method returns “qwerty”, and then when the test fails see what it returns and then amend the test to expect that thing.

Other stuff

There was a good QA at the end; one tip he mentioned to help deal with flapping tests was if the issue is due to a race condition, where something else might always not have completed before the test tries to click on the next step in the sequence, he might write an extra expectation for the page to “have content such-and-such” first before trying the click, looking for something that appears on the page once the action has completed – this will always wait a reasonable time before timing out, rather than failing instantly like the click instruction.