Recently, an Arduino project forced me to brush up on my C. Like many programmers of my generation, C was my first programming language; but it has been a while since I wrote anything in it. After a quick K&R refresher, I immediately began looking for a unit testing framework. I found several, but I had trouble setting them up. Finally, I came across Ceedling.
Ceedling is a Ruby gem that takes care of all the setup, building, and running of C unit tests. It comes with a simple testing framework, a mocking library, and uses Ruby’s Rake to build and run your C tests. Let’s take a look at how to install and use Ceedling.
The first thing we need to do is install Ceedling.
Ceedling comes with a command-line tool that can be used to generate a project.
The directory structure should be pretty self-explanatory:
In Ceedling, Rake (Ruby Make) is your build tool. Each Ceedling project includes several helpful Rake tasks.
Let’s use the module:create
task to generate a source and test file for a C module representing a simple point.
A quick run of our test verifies everything is setup correctly.
The output is a little verbose, but it looks like everything compiled and ran successfully. Our one failing test is the sample test generated from the Rake task. Let’s replace this with a real test.
Ceedling includes its own test framework: Unity. Unity is a simple, unit testing framework built in the style of xUnit testing frameworks, i.e.:
setUp
functiontearDown
functionTEST_ASSERT_TRUE
TEST_ASSERT_EQUAL_INT
, TEST_ASSERT_EQUAL_STRING
, etc.Let’s write a simple test for our point module.
test/test_point.c
First, Unity is included, then our module’s interface. Even though we don’t have any setup or cleanup for our test, it’s still required to implement the setUp
and tearDown
functions. Running this gives us an error.
We need to define the point struct
and declare MakePoint
in our module’s header file.
src/point.h
A re-run of our test shows that we’re making progress.
Implementing MakePoint
should get our test passing.
src/point.c
Ok, we got our first test passing. That wasn’t too bad. I especially like how Rake hid all the compiling, linking, and running of our code.
Now that we have the basics down, let’s introduce a dependency in our point module. In order to isolate our point module tests from this dependency, we’ll need to mock it.
Ceedling includes a simple, straightforward mocking library: CMock. CMock automatically generates mock functions for each function declared in a module.
Continuing with our example, let’s add a function to draw a point. We’ll introduce a separate display module that’s responsible for drawing.
After generating the display module, we can now use CMock to mock it out.
test/test_point.c
The first step is to include “mock_display.h”. CMock created this file from src/display.h. Several mock functions are defined for each function in src/display.h. We used the generated “<function-name>_Expect” mock function. If we needed to return a value, a “<function-name>_ExpectAndReturn” mock function is also available. Let’s use the test:pattern
Rake task to run just this point test.
The error message tells us that we need to declare DrawPoint
and Draw_Int_Expect
. Draw_Int_Expect
would have been generated by CMock if we had a Draw_Int
function. So let’s declare Draw_Int
in our display module, but, because we’re mocking it, defer its implementation. We’ll declare, and for the sake of getting a good test failure, implement a no-op DrawPoint
function in our point module.
src/display.h
src/point.h
src/point.c
The failure tells us our expectations weren’t met. Let’s implement DrawPoint
and get it to pass.
src/point.c
After years away from C, it’s refreshing to see that it hasn’t been left behind by the testing community. Ceedling is a powerful tool that uses Ruby to hide the tedious C plumbling. It’s a great way to start test-driving your C code today.