šTest-driven development for embedded c
- authors
- Grenning, James
- year
- 2011
You can test external libraries
_
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, be definition, not smart enough to debug it. āBrian Kernigan
(p.2)
The biggest schedule killers are unknowns; only testing and running code and hardware will reveal the existence of these unknowns.
(p.4) software is fragile
immediate feedback
show diagram with cost of a bug and where TDD stands
with Debug Later Development the feedback is too late to help you learn from your mistakes
Iāll be discussing a very fine-grained test increments. Once youāll get comfortable with TDD, you can move in larger steps. Anyway, I do recommend trying to move with small increments.
TDD micro-cycle
Add a small test
Run all the tests and see the new one fail, maybe not even compile
Make the small changes needed to pass the test.
Run all the tests and see the new one pass.
Refactor to remove duplication and improve expressiveness.
^ Red-Green-Refactor
how to work with legacy code
p.8 - TDD benefits
run tests on development system, not a target platform
automatically build and run tests whenever a file is saved
cmocka
modularity is needed to make testable code. Also, the modular design is a natural outcome of TDD (p.27)
typedef struct CircularBufferStruct *CircularBuffer
you can define pointers to forward-declared structs. it is fine to use them as long as you do not dereference. the struct then is defined in the .c file and is āprivateā
(p.27) ADT definition by Barbara Liskov
keep test list (itās just for you. donāt work too hard creating it)
āvirtual deviceā
dependency injection
(pass MMIO addresses as parameters)
it is tempting to write the code that you know you will need---donāt do it. Youāll write them after a test that requires that.
3 laws of TDD (p.37)
Do not write production code unless it is to make a failing unit test pass.
Do not write more of a unit test than is sufficient to fail, and build failures are failures.
Do not write more production code than is sufficient to pass the one failing unit test.
q
The code behind the interface starts with hard-coded return results, so it feels like nothing is being tested. The point is not testing but driving the interface design and getting the simple boundary tests in place. ---p.37
starting with failing test ensures the test is correct
āfake it till you make itā (p.43)
You stop faking as soon as it is more trouble to fake it than it is to make it.
Keep tests small, focused
refactor on green
tests are code, too---refactor
q
Idealism increases in direct proportion to oneās distance from the problem. ---John Galsworthy
ādo you have a test for that?ā
the question is esp. useful in pair-programming
when test fails, donāt debug. undo and inspect your work
dual-targeting - your code is designed to run on both target platform and development platform
you donāt need hardware to develop and debug most of the issues
long upload (flashing) times -> lead to larger batch increments -> more rare feedback -> more can go wrong
dual-targeting affects architecture but in a good way
there are pitfalls to dual-targeting - different compilers could have different bugs, different headers, different data sizes
when dual-targeting from the start, it is easier to port on new platform in the future
Embedded TDD cycle:
TDD microcycle
Compiler compatibility check (cross-compile to target)
Run tests on eval board
Run on target hardware
Run acceptance tests on hardware
extract platform-specific code to its own directory and use linker + compiler (preferably not pre-processor)
testing with hardware
automated hardware tests (āhardware acceptance testsā)
partially-automated (e.g., look that the correct LED is on)
tests with external instrumentation
p.91 āTDD helps you go fasterā
TDD != unit-testing
p.97 benefits of TDD over test-after development
TDD influences design (in a good way)
TDD prevents defects
TDD saves time debugging
TDD is more rigorous and provides better tests coverage
test-double (stunt-double = Š“ŃŠ±Š»ŠµŃ) impersonates some function, data, module, or library