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:
- build – build products
- project.yml – Ceedling configuration
- rakefile.rb – Ruby Rake configuration
- src – source files (non-test code)
- test – test files
- vendor – where Ceedling lives
Building with Rake
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.
Testing with Unity
Ceedling includes its own test framework: Unity. Unity is a simple, unit testing framework built in the style of xUnit testing frameworks, i.e.:
- Test function names must start with “test”
- Per-test setup is done in a
- Per-test cleanup is done in a
- Various assertion functions can be used to verify behavior e.g.,
Let’s write a simple test for our point module.
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
tearDown functions. Running this gives us an error.
We need to define the point
struct and declare
MakePoint in our module’s header file.
A re-run of our test shows that we’re making progress.
MakePoint should get our test passing.
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.
Mocking with CMock
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.
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
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.
The failure tells us our expectations weren’t met. Let’s implement
DrawPoint and get it to pass.
Start Testing Today
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.