Portrait of me in my natural habitat

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.