I am writing an IMU device driver in C using test-driven development methodology. As part of the driver, I pass a struct with user selected configuration settings to an initialization function which configures the device by writing into its individual registers.

Using my interpretation of approach described in James Grenning's book "Test Driven Development for Embedded C", I ended up writing a bunch of tests which basically mock every function call (since every function call is an SPI_Write() function), resulting in brittle tests that mimic code implementation. Add or modifying initialization has become a pain as most tests need to be rewritten.

Is there a better way to initialize devices using TDD?

  • If I were you, I would write an emulator for the device and have my driver interact with the emulator. from my emulator I would exchange signals with the driver covering different test cases. – A.Rashad Nov 12 '18 at 16:52
  • 2
    @A.Rashad He wrote a mock, which is reminiscent of an interface emulator. A mock can't emulate anything useful; it can only expect things. The problem with the real functional emulators is that they themselves take a lot of coding and testing. – Nick Alexeev Nov 12 '18 at 18:08

When you are writing a device driver that is poking magic values into the registers of a device, then in my view it is impossible to write reliable unittests for this driver. And it doesn't matter much if those registers are made available directly in the address space of the processor or if the device is on the other end of a bus.

When communicating with external devices on the driver level, the order and timing of the interaction with the device can be critical and that can not be verified reliably with unittests, especially not if those unittests are being executed on a different processor architecture (e.g. unittesting a project for an ARM board on your development PC).
For that reason, I often recommend to forego unittesting the lower-level drivers, and to test them primarily in the integration with the real hardware. An exception might be a driver for a device on a bus system that communicates with a well-defined protocol. Then the unittests can verify that the correct messages are sent on the bus.

| improve this answer | |

Disclaimer: my experiences with the realities of embedded are limited. You may find this guidance doesn't match well with the constraints you are operating under.

TDD loves functions. Strange, given its lineage, but so it goes. It's often the case that your starting point is to think, and in doing so discover a function that encapsulates most of the logical complexity of your problem, then concentrate your early efforts on generating tests that induce the implementation of that logic.

In this case, it might be a function that accepts your configuration settings as an argument, and returns a linked list of descriptions of calls to set registers (which is to say, an identifier for each function, and the arguments to pass to it -- the sort of thing you could pass to a dumb switch statement).

You would then flesh out the implementation of this function by specifying variations of configurations, and what constraint needs to be satisfied by the returned result.

The earliest test would be chosen to select the simplest list of calls, and then additional tests would be ordered based on which example introduces the minimal amount of new complexity into the production code.

If return values from earlier function calls are part of the logic for computing later calls, then you may be looking for something more like a protocol library (see this talk by Cory Benfield)

Add or modifying initialization has become a pain as most tests need to be rewritten.

Extending the implementation to meet new requirements should be relatively painless, given the earlier behaviors don't change.

Modification -- meaning the actual requirement has changed; yeah, that can be painful.

Sometimes this indicates that the tests overfit the implementation. This symptom is common in UI tests, where cosmetic changes make the result look significantly different to the test harness, and so you may be running into something similar.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.