Test-Driven C with Ceedling

Posted on by in Everything Else

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.

Getting Started

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 setUp function
  • Per-test cleanup is done in a tearDown function
  • Various assertion functions can be used to verify behavior e.g., TEST_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.

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.

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

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.