Select Page

Test-Driven Development of Embedded Systems – Part 2: The Three TDD Rules of Small Steps

Test-Driven Development (TDD) is the implementation of the test-first approach in component testing and involves writing unit test cases before the actual implementation. Adherence to just three rules and a few tricks for dealing with the target hardware bottleneck makes TDD possible even for embedded systems.

Test-Driven Development can be described as follows: Bob Martins Described by three simple rules:

  1. Do not write code unless it is necessary for the successful execution of a test case.
  2. Limit the scope of a unit test case to one that fails. Compiler/linker errors are also errors.
  3. Do not write more code than is necessary to pass the current unit test case.

TDD test-driven development, Figure 2

Figure: The three TDD rules of small steps

According to rule 1, developing a feature begins with creating a unit test. However, according to rule 2, you can't test too much in this unit test, because as soon as the unit test can fail, you have to stop developing the unit test and write production code to pass the test, but nothing more, as otherwise rule 3 would be violated.

Small steps with the tester leg

Adhering to these three TDD rules constantly reminds us that we must move towards our goal in very small, iterative steps. Let's assume our left leg is our tester leg, while our right leg is our developer leg (see Figure 3). We start each iteration of the TDD cycle with the tester leg because starting with the developer leg would violate Rule 1. If we take too large a step with the tester leg, there's a high risk that errors will slip through during later refactoring and remain undetected until they painfully surface. Rule 2, therefore, forces us to take very small steps with the tester leg, thus making refactoring easier. If you're unsure whether a step might be too large, it's always better to opt for several smaller ones.

We can finally take a step forward with our developer leg, but according to rule 3, not past the tester leg that has already jumped ahead, but only to the same level. We are now standing with both feet side by side on the ground again. This is precisely the only permissible starting point for optionally inserting a refactoring, in order to take the next small step towards the goal after passing all the test steps completed up to that point, naturally with the tester leg first – or would you rather get up on the wrong side?

Target Hardware Bottleneck

The target hardware bottleneck, an obstacle to TDD (Technology-Driven Design) of embedded systems, arises specifically when target hardware and software are developed simultaneously. During software development and testing, no target hardware is yet available to implement TDD. This is compounded by problems with automated test suites on the embedded system, limited resources, and long flash load cycles.

TDD on embedded systems therefore requires special procedures to address these problems.

One prerequisite for circumventing the aforementioned problems is dual-targeting. From day one, the software is developed to be largely platform-independent, or at least for two platforms: the development platform and the target platform. This enables development at a constant speed, avoids problems with the accumulation of untested code, and allows code testing before the target hardware is available.

Based on dual-targeting, various test strategies can be applied:

  1. Test on Host

For software components that are largely hardware-independent, the development platform (host), usually a PC, can be used as a test environment. The test tool and the software under test run on the host. This combines advantages such as avoiding flashing times, very short cycle times, and convenient test tools. The disadvantage is the need to repeat the tests later on the target system to verify their functionality. Besides professional tools like Tessy or Parasoft C++, free tools such as Google Test and Mock are also suitable.

  1. Test on Target

Hardware-dependent software components, such as device drivers, must be tested on the target platform. To save time, it is quite possible to initially develop these software components using a test-on-host strategy, simulating the hardware dependency with mocks. As soon as the target hardware becomes available, the tests should then be repeated using a test-on-target strategy. The test tools used must run on the target. Only small, and therefore functionally limited, test tools, such as Embedded Unit or CppUnit, can be used.

  1. Remote Testing

Remote testing combines the advantages of test-on-host and test-on-target strategies. The basic idea is to run the test tool on the host and execute hardware-dependent tests first on the host and later on the target via interprocess communication. Suitable test tools, such as Tessy, support this approach without requiring any modifications to the tests themselves. Interprocess communication often doesn't occur directly with the target, but rather between the test tool and the development environment. The test tool instrumentes the code and uploads it to the target for test execution via the development environment. The test results are then read back via the debugger interface and displayed in the test tool. From the user's perspective, the result of a test run on the host and the target appears identical. Therefore, the user only needs to switch the target and must wait a little longer for the test results due to the upload/download process.

Important: TDD does not replace testing!

TDD can also be successfully used on embedded systems. The quality of unit-tested deliverables for integration, system, and acceptance testing will certainly increase compared to traditionally unit-tested software. At best, you can expect fewer detected errors in the system test (40-50%)..

However, the last, particularly promising statement also makes it clear:

Test-Driven Development does not replace testing itself; rather, it is a structured method of developer-oriented unit testing alongside code development.

Integration, system and acceptance tests can very advantageously use the test-first approach, but must follow TDD for a high-quality result.

Part 1 of the article It covers the test-first approach and the TDD cycle.

Further information

MicroConsult Training & Coaching on the subject of Test & Debug

MicroConsult expertise in the field of Test & Debug

MicroConsult Training & Coaching on the topic Quality, Safety & Security

MicroConsult expertise in the areas of quality, safety & security

MicroConsult Newsletter

With the MicroConsult newsletter, you'll stay on the pulse of the embedded world. Look forward to proven practical knowledge, real professional tips, and current events – directly from our experts for your project success.

Subscribe now!

Published by

Alfred Ressenig

Alfred Ressenig