What best practices have you used in unit testing embedded software that are peculiar to embedded systems?
What is unit testing in embedded systems?
Unit testing is a method of testing software where individual software components are isolated and tested for correctness. Ideally, these unit tests are able to cover most if not all of the code paths, argument bounds, and failure cases of the software under test.
What are the two methods of testing the system in embedded?
Software testing is performed on client-server oriented applications and software. Embedded testing is executed on both software and hardware. It is generally based on black box testing. It can be based on both white and black box testing.
Can you unit test firmware?
Unit testing is now a widely accepted software engineering practice. However, unit testing is severely under-utilized in the world of embedded firmware because of a few myths. Unit testing involves (usually automated) testing of small software “units” in a much larger program.
How are embedded systems tested?
Embedded software can be tested either on-target or on-host. In on-target testing, an application is tested on the hardware to be deployed (the target). In on-host testing, an application is tested on a host computer that has a different hardware environment to the final application.
Embedded software may have come a long way in the last 10 years but we generally did the following:
- for algorithms that didn't depend on the target hardware, we simply had unit tests that were built and tested on a non-embedded platform.
- for stuff that did require the hardware, unit tests were conditionally compiled into the code to use whatever hardware was available. In our case, it was a serial port on the target pushing the results to another, more capable, machine where the tests were checked for correctness.
- Depending on the hardware, you could sometimes dummy up a "virtual" device on a non-embedded platform. This usually consisted of having another thread of execution (or signal function) changing memory used by the program. Useful for memory mapped I/O but not IRQs and such.
- typically, you could only unit test a small subset of the complete code at a time (due to memory constraints).
- for testing of time-sensitive things, we didn't. Plain and simple. The hardware we used (8051 and 68302) was not always functional if it ran too slow. That sort of debugging had to be done initially with a CRO (oscilloscope) and (when we had some more money) an ICE (in-circuit emulator).
Hopefully the situation has improved since I last did it. I wouldn't wish that pain on my worst enemy.
There can be a lot to be gained by unit testing in a PC environment (compiling your code with a PC C compiler and running your code in a PC unit testing framework), with several provisos:
- This doesn't apply to testing your low-level code, including start-up code, RAM tests, hardware drivers. You'll have to use more direct unit testing of those.
- Your embedded system's compiler has to be trustworthy, so you're not hunting for bugs created by the compiler.
- Your code has to be layered architecture, with hardware abstraction. You may need to write hardware driver simulators for your PC unit testing framework.
- You should always use the
stdint.h
types such as uint16_t
rather than plain unsigned int
etc.
We've followed these rules, and found that after unit testing the application-layer code in a PC unit test framework, we can have a good amount of confidence that it works well.
Advantages of unit testing on the PC platform:
- You don't face the problem of running out of ROM space on your embedded platform due to adding a unit testing framework.
- The compile-link-run cycle is typically faster and simpler on the PC platform (and avoids the 'write/download' step which can potentially be several minutes).
- You have more options for visualising progress (some embedded applications have limited I/O peripherals), storing input/output data for analysis, running more time-consuming tests.
- You can use readily available PC-based unit test frameworks that aren't available/suitable for an embedded platform.