Test-Driven Development for Embedded C is hands-down the best book on the subject. This is an amiable, readable book with an easy style that is fairly code-centric, taking the reader from the essence of TDD through mastery using detailed examples. It’s a welcome addition to the genre because the book is completely C-focused, unlike so many others, and is specifically for those of us writing firmware.
James skips no steps and leads one through the gritty details but always keeps the discussion grounded so one is not left confused by the particulars. The discussion is laced with homey advice and great insight. He’s not reluctant to draw on the wisdom of others, which gives the book a sense of completeness.
The early phases of a TDD project are mundane to the point of seeming pointlessness. One writes tests to ensure that the most elemental of things work correctly. Why bother checking to see that what is essentially a simple write works correctly? I’ve tossed a couple of books on the floor in disgust at this seeming waste of time, but James warns the gentle reader to adopt patience, with a promise, later fulfilled, that he’ll show how the process is a gestalt that yields great code.
TDD does mean one is buried in the details of a particular method or a par- ticular test, and the path ahead can be obscured by the tests at hand. If you’re a TDD cynic or novice, be sure to read the entire book before forming any judgments so you can see how the details morph into a complete system accompanied by a stable of tests.
Better than any book I’ve read on the subject, Test-Driven Development for Embedded C lays out the essential contrast between TDD and the more con- ventional write-a-lot-of-code-and-start-debugging style for working. With the latter technique, we’re feeding chili dogs to our ulcers as the bugs stem from work we did long ago and are correspondingly hard to find. TDD, on the other hand, means today’s bug is a result of work one did ten minutes ago. They’re exposed, like ecdysiast Gypsy Rose Lee’s, uh, assets. A test fails? Well, the bug must be in the last thing you did.
One of TDD’s core strengths is the testing of boundary conditions. My file of embedded disasters reeks of expensive failures caused by code that failed because of overflows, off-by-one errors, and the like. TDD—or, at least James’ approach to it—means getting the “happy” path working and tested and then writing tests to ensure each and every boundary condition is also tested. Conventional unit testing is rarely so extensive and effective.
Embedded TDD revolves around creating a test harness, which is a software package that allows a programmer to express how production code should behave. James delves into both Unity and CppUTest in detail. (Despite its name, the latter supports both C++ and C). Each test invokes creation and teardown routines to set up and remove the proper environment, like, for instance, initializing a buffer and then checking for buffer overflows. I found that very cool.
Test-Driven Development for Embedded C is an active-voice work packed with practical advice and useful aphorisms, such as “refactor on green” (get the code working first, and when the tests pass, then you can improve the code if necessary). Above all, the book stresses having fun while doing development. And that’s why most of us got into this field in the first place.