Good Code: Part Four of Five – Testable Code

Welcome back to our five week series, where we explore five aspects of code that should really be fundamental to code being described as “good”. This week we bring you part four. So far we have covered Correctness, Understandability, and Efficiency.

Much of the series is built upon Sara Falamaki’s talk, entitled “Happy Hackers == Happy Code”, which Eric attended at Linux.conf.au 2010 in Wellington.

Feature Four: Good code should be testable

As the scale of your code increases, testability becomes an extremely important factor. Manual tests are possible for smaller code-bases, but automated testing quickly becomes paramount.  So, in order for code to be truly ‘good’, it needs to be crafted in such a way that it can be tested automatically.

The cost of bugs can be staggering, as demonstrated by a 2002 NIST survey which can be found here. There’s a particularly good graphic on display at SuperWebDeveloper which illustrates the alarming escalation of cost; Wikipedia also has a section on the Economics of Software Testing.Take particular note of how quickly the cost rises; a bug introduced at the requirements stage can cost up to 100 times more to fix post-release than one identified immediately.

Automatic test suites (run by computer) are not only much faster than manual tests; they’re also vastly more thorough.  If there are 1200 different things that could possibly happen in a program, an extremely skilled tester might manage to test half of the scenarios.  Perhaps more, but one thing is certain – a human tester won’t be able to test every scenario. Even worse, with every change and code modification, it isn’t sufficient to simply test the new parts – the entire code base must be tested again, in order to identify and repair any regression bugs.  This is where an automated test suite comes to the fore.

There are a few different methods of creating your tests, but the holy grail of testing must be test-driven development.  In this style of development, the developer first writes the test which will determine whether the code is operating properly.  Of course, this test will fail, because at this stage the developer hasn’t yet written the code.  Step two is to run all of the tests – only the last one should fail.  This step ensures that the entire test suite is still working; it also ensures that the recently written test is written correctly – the program should not pass this test, as the code simply doesn’t exist yet.  If the test passes, the test itself is incorrect.

Once the developer has confirmed that the existence of the new test hasn’t broken the test suite, and confirmed that the new test is not passing, it is now time to write the code.  The code is considered complete when all the tests are successful.  This ensures that the new code works as required, as well as confirming that the new code hasn’t broken any of the other code!

The more testable your code, the fewer problems you will have to deal with down the line.