Embedded software has all the challenges of “regular” software, like poor quality and unreliable schedules. It is just software with some additional challenges. The additional challenges do not disqualify TDD for embedded. TDD even helps with some of those uniquely embedded challenges.
Leaving embedded out of it for a moment, here are benefits that TDD practitioners report:
- Fewer bugs
Small and large errors in logic, that can have grave consequences in the field, are found quickly during development.
- Less debug time
Fewer bugs means less debug time. That’s only logical Mr. Spock.
- Fewer side effect defects
Tests capture assumptions, constraints and representative usage.When new code violates a constraint or assumption, the tests holler.
- Documentation that does not lie
Well structured tests become a form of executable and unambiguous documentation. A working example is worth a thousand words.
- Peace of mind
Having thoroughly tested code with a comprehensive regression test suite gives confidence. TDD developers report better sleep patterns, and more uninterrupted weekends.
- Improved design
A good design is a testable design. Tests provide an early warning of lost testability. Long functions, tight coupling, complex conditionals all lead to more complex and less testable code. The need, desire, and discipline to test helps get subtly bad design from deteriorating into a horrendously bad designs. TDD is a code rot radar.
- Progress monitor
The tests keep track of exactly what is working and how much work is done. It gives you another thing to estimate and a good definition of done.
- TDD is fun!
It’s like a game where you navigate a maze of technical decisions that lead to highly robust software while avoiding the quagmire of long debug sessions. With each test there is a renewed sense of accomplishment and clear progress toward the goal. Automated tests record assumptions, capture decisions, and free the mind to focus on the next challenge.
Where can TDD Help Embedded?
Embedded software has all the challenges of “regular” software, like poor quality and unreliable schedules. It is just software with some additional challenges. The fact that there are additional challenges does not prove that TDD can’t work for embedded. Maybe it can help with some of the uniquely embedded software challenges.
My Big AH-HAH!
When I was first exposed to TDD at the first Extreme Programming Immersion(tm) back in 1999, I was working on a team creating an embedded communications system. We had just begun extracting use cases from the project’s requirements document when I took a week away from the client to attend the Immersion. Our plan was to create an Object Oriented architecture and document it in UML. All that changed after getting first hand knowledge of Extreme Programming and TDD.
Like many embedded development efforts, having the product release held up by software development was nothing new. Hardware availability chokes progress to a slow drip until the target hardware bottleneck is opened. This project was no exception. The hardware platform was still unknown. The target operating system was not chosen. Peripherals were even more mysterious, and a long way off. But, there was a lot of software to do. So the team was ready to do what they usually did:
have meetings, talk, argue, dream, and document the software they might write.
After a week of immersion in Extreme Programming, the big ah-hah hit me! We can do more than document and wait. We can take action. We can make solid progress the code.
With open source tools, we created a TDD style test bed on the development system. Object Oriented Design principles allowed us to manage the dependencies on the hardware, creating a simple abstraction layer as we discover the essence of what the core application needs from the hardware. The code was not burdened with the bits and bytes of the low level hardware interface. We defined the services needed from the hardware and programmed to those interfaces.
Obviously, we could not finish before we had hardware, but we could and did get a great start before the hardware and OS were decided. TDD and OOD together meant plenty of progress can be made before the hardware is ready.
Management was willing to take a chance; they were open to new, creative, and common sense ideas we found in the technical practices of XP. Specifically, with TDD, we could exercise the design ideas before the hardware was ready, getting significant parts of the core system logic running very early in the development life cycle.
The project manager was very excited that he could see working parts of the system one month into the project. We wrote a small domain specific scripting language to describe functional tests. They read like use cases and allowed the system engineer to verify the behavior of the core system logic, the company’s intellectual property, continually through the project not just at the end. After two months, using our domain specific language, we built functional tests to stress the system to some of its most challenging design goals, eliminating considerable risk and endless analysis paralysis.
We made a fundamental shift. Rather than working on paper artifacts, we worked on what would be deployed: the code. On prior projects, with two months of effort, engineering only delivered documents describing what might be built. With TDD, the code speaks for itself early in the product development timeline. We had documents too, but unlike before we knew the designs worked because our tests and simulations passed. We had a great indicator of progress, and significantly reduced the risk of the software holding up the deployment.
Waiting is Expensive
Every embedded developer has experienced the target hardware bottle- neck. Often the hardware is concurrently developed with the software, and unavailable for much of the development cycle. If that is not bad enough, both the hardware and the software have bugs, and it’s not always clear where the bugs are hiding.
Some systems have very expensive hardware; so expensive that there is no way for each developer to have their own target system that is ready when they are. When developers don’t have ready access to an execution platform they have to wait, and waiting is very expensive.
When I first experienced TDD it immediately dawned on me that it is a technique that embedded developers need. They need it to help solve the target hardware bottleneck. Code developed TDD style can be tested on a convenient system, your development system, and later tested on the target hardware. With TDD, you can get considerable confidence in the software before integrating with the hardware. Sure there will be parts of the software that can only be tested with the hardware, but those parts can be minimized and great progress can be made before the hardware is ready. No need to wait.
Benefits for Embedded
The embedded developer can expect the same benefits described earlier that non-embedded developers enjoy, plus a few bonus benefits specific to embedded:
- Reduce risk by verifying production code, independent of hardware, before hardware is ready or when hardware is expensive and scarce.
- Reduce the number of long target compile, link, and download cycles that are executed by removing bugs on the development system.
- Reduce debug time on the target hardware, where problems are more difficult to find and fix.
- Isolate hardware/software interaction problems by modeling hardware interactions in the tests.
- Improve software design through the decoupling of modules from each other, and the hardware. Testable code is, by necessity, modular.
In another article, I’ll describe how an embedded team might customize their use of TDD.
If you want to see how close you can TDD to the hardware, see these two articles:
Who says you can’t test drive a device driver?
Now I’ll really use test driven development to write device driver code
Progress Before Hardware
I’m glad to hear such sentiment. A few years ago I and two others started a company (http://www.agilerules.com) focused on Agile coaching, training, and readiness assessment, but with a target market of the embedded world. We created an open source tool for unit and acceptance testing embedded code with an unusual twist; We used GDB’s ability to communicate with the board to drive the testing from the host computer, so which tests got run was controlled by the host, only the data for the current test need be on the board’s memory, and results were captured on the host and a red/green HTML page generated from it. It was called CATSRunner (http://www.agilerules.com/projects/catsrunner).
I will admit we didn’t get many customers in that market, but we got a lot of interest. Now that Agile and TDD are more accepted, I hope more people see the wisdom of more testing. With embedded in so many consumer and in-the-field products, “upgrading is a xxxxx”.
I think your final point (slightly edited :-)) is that more testing might mean more field updates can be prevented.
Nice description of TDD benefits. We have a team starting to talk about TDD, but they are still skeptical. This will be lots of food for discussion!
The page for CATSRunner shows the latest version at 2005. The system sounds exciting and dovetails into our desire to move development on to GNU tools. Is CATSRunner still alive or just on life support?
I can’t say about CatsRunner, but If you are using the GNU tool chain, you might want to look at CppUTest. Another good test harness of unity.
I’m looking forward to your book, “Test Driven Development for Embedded C”.
I’m very interested to see how that will work.