Why does my firmware need … unit-tests?

It’s nice to have a suite of tests that confirms our public/API methods work as they should, especially if we’re trying to decide if the current build is ready for acceptance testing. But that only makes unit-testing ‘good to have’. We need unit-tests because they enable refactoring. The argument goes like this:

  • Software rot is terminal. A risky and expensive rewrite is unavoidable.
  • The only thing (I know of) that can prevent software rot is refactoring.
  • Refactoring without a way to check we haven’t introduced new bugs into our production code is reckless and irresponsible!
  • Unit-tests check that these changes haven’t introduced new bugs into our code.
  • Therefore unit-tests are essential to keep our code extensible and maintainable.

This is why our firmware needs unit-tests. But unit-testing can deliver more, if we also adopt test-driven design (TDD). Test-after (writing the code) is tedious in the extreme. Therefore, being human, we will — in practice and in general — avoid writing them if we can. The test-first approach intersperses writing tests and production code, making it much less tedious, and encouraging us to write more and better tests. But that isn’t the main benefit of TDD.

For experienced practitioners, the mnemonic “red, green, refactor” is sufficient. But to see the benefits of TDD, we need to consider it in more detail:

  • Add a test for a feature to be added to the method-under-test.
  • Run all tests and confirm the new one fails.
  • Write the simplest code that will pass the new test.
  • Run all tests and confirm they pass.
  • Refactor code.
  • Run all tests and confirm they pass.
  • Repeat (if there are still features to be added).
  • The method-under-test is complete.

Before we write a test, we must know what we want to test. TDD makes us focus on the requirements before we write the code. XP/Lean/Agile wisely warns us away from too much up-front design. Here, TDD helps us check we have done just enough.

Odd as it may seem, there is no obvious way to know exactly when you have finished coding a method. Often, we just stop when we feel we’ve done enough. TDD tells us when we’ve finished: when all required features are present, tested and working.

The benefits of refactoring cannot be over-emphasised, in my opinion. It’s not just the (rather negative) avoidance of software rot, it’s the greater coherence our designs have. There are lots of ‘code smells’ that prompt us to refactor, but the simplest is: if you spot a bolt-on in your code, refactor until it belongs. This is particularly relevant during TDD, where we first just code a solution, then refactor it into the local design structure.

Finally, we come to the most obvious benefit of all: TDD really does reduce bugs. Microsoft and IBM did some research, and found bug-counts reduced by 60–90%.

This is the second in a short series of short blog posts. Their main (intended) purpose is to stimulate discussion, so please leave a comment. Let us know what you think. Thanks for dropping by!

Why does my firmware need … unit-tests?

One thought on “Why does my firmware need … unit-tests?

  1. mattchernosky says:

    “Before we write a test, we must know what we want to test.”

    And thus… what we want our code to do!

    Thinking about a single test forces me to think “what is the next thing I want this code to do?” It takes a complicated problem (the development of a software module) a breaks it down into a simpler, linear series of steps. These are easier for my brain to execute.

    Ooh, software rot. This is a particular problem for firmware in that many systems can have very long lifetimes. We already have all this hardware in the field… we just need to add this one feature… and another one… and another one. Been there!

    Wouldn’t we love to do a rewrite? But how, if we’re not sure exactly what it’s doing anymore?

    Liked by 1 person

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 )

Google photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s