Unit Testing in C Simplified with Unity and Ceedling
Posted 2021-02-16
tl;dr ThrowTheSwitch.com’s Unity is a great unit testing tool for C, and when paired with Ceedling the time to first test is very low.
I like automated testing. It gives me the confidence my code works as I’m writing, and it makes sure I don’t introduce a regression. My comfort with working in any particular language or framework often comes from the quality of the testing tools in their ecosystem.
A recently project forced me to come back to an old friend: C. Unit testing in C has always been a bit daunting. Frameworks exist (Check and CUnit for example), but any forays into integrating them with my projects has proved fruitless. Being primarily a web developer, my outsider’s perspective on build systems in C’s ecosystem is one of bewilderment. It’s not to say they’re bad or wrong, just not something I can wrap my head around.
Enter ThrowTheSwitch.com. This site is a cornucopia of C programming resources. They have libraries for exceptions, mocking, and the ones I had come searching for: unit testing and a build system. Unity — not to be confused with the game engine — comes with many of the basic features one would expect from a testing library. Additionally, their Ceedling project makes setting up projects integrated with Unity a breeze. Running the command ceedling new fibbonacci
creates a project with a starter source and test directory set up and ready to go.
Bringing it all together, I can write a simple test for a Fibonacci function like this:
// test/test_fibonacci.c
#include "unity.h"
#include "fibonacci.h"
void test_fibonacci(void) {
TEST_ASSERT_EQUAL_INT(1, fibbonacci(1));
TEST_ASSERT_EQUAL_INT(1, fibbonacci(2));
TEST_ASSERT_EQUAL_INT(2, fibbonacci(3));
TEST_ASSERT_EQUAL_INT(55, fibbonacci(10));
}
In my header file I include the method signature and a blank implementation in my source file so my test will compile and run.
// src/fibonacci.h
#ifndef FIBONACCI_H
#define FIBONACCI_H
int fibbonacci(int i);
#endif // FIBONACCI_H
// src/fibonacci.c
#include "fibonacci.h"
int fibbonacci(int i) {
return 0;
}
Now I have a failing test to start my TDD cycle.
⇒ ceedling test
Test 'test_fibonacci.c'
-----------------------
Compiling fibonacci.c...
Linking test_fibonacci.out...
Running test_fibonacci.out...
-------------------
FAILED TEST SUMMARY
-------------------
[test_fibonacci.c]
Test: test_fibonacci
At line (7): "Expected 1 Was 0"
--------------------
OVERALL TEST SUMMARY
--------------------
TESTED: 1
PASSED: 0
FAILED: 1
IGNORED: 0
---------------------
BUILD FAILURE SUMMARY
---------------------
Unit test failures.
I chose to implement my fibonacci with recursion:
// src/fibonacci.c
#include "fibonacci.h"
int fibbonacci(int i) {
if (i <= 1) {
return i;
} else {
return fibbonacci(i - 1) + fibbonacci(i - 2);
}
}
After running the test again I can see it passes:
⇒ ceedling test
Test 'test_fibonacci.c'
-----------------------
Generating runner for test_fibonacci.c...
Compiling test_fibonacci_runner.c...
Compiling test_fibonacci.c...
Linking test_fibonacci.out...
Running test_fibonacci.out...
--------------------
OVERALL TEST SUMMARY
--------------------
TESTED: 1
PASSED: 1
FAILED: 0
IGNORED: 0
For more on Ceedling check out their extensive github docs. Likewise, you can find more about Unity on GitHub, including a complete list of available assertions.