Spinning Up in CMock

I recently started on a large project that utilizes the powerful CMock mocking library in its testing. I tried to find some simple tutorials to get a better understanding of this library. But though there’s a lot of helpful documentation on CMock, I found little advice on getting to know it in general. So here is my quick tutorial on how to get started with CMock.

CMock Overview

CMock is an automated stub and mock generation framework made by a community of C developers, including Atom Greg Williams. It works within the Unity testing framework, and it is automatically included in any projects that use the Ceedling build management tool.

CMock autogenerates all functions defined in your code’s header files. It not only generates them, but it gives you additional functionality around those calls.

  • Expectations allow you to specify functions that you expect to be called with specific values by the function under test.
  • Returns allow you to specify what data the mocked function should return to the tested code.
  • Ignores allow you to tell the code to ignore values from expectations, if they’re not relevant to the test.
  • Callbacks allow you to specify more complex mocked functionality.

The power of CMock really shines when using it. Let’s get ready to watch it work!

Let’s Spin Up a Project!

Install Ceedling

To get started, we’re first going to install Ceedling using the command

gem install ceedling

Then generate a new Ceedling project with

ceedling new cmock_example

Make testable code

In order to write tests, we need testable code. For this example, I created two simple classes. The first is a rectangle class that takes in height and width values and a few basic functions. The second is a shape_container class that creates a rectangle and operates on it.


typedef struct rectangle
{
    uint16_t r_length;
    uint16_t r_width;
} rectangle_t;

static rectangle_t rect;

void rectangle_init(uint16_t length, uint16_t width)
{
    rect.r_length = length;
    rect.r_width = width;
}

uint16_t rectangle_get_area(void)
{
    return rect.r_width * rect.r_length;
}

uint16_t rectangle_get_perimeter(void)
{
    return (rect.r_length + rect.r_width) * 2;
}

bool rectangle_is_square(void)
{
    return (rect.r_length == rect.r_width);
}

void shape_container_init(uint16_t r_length, uint16_t r_width)
{
    rectangle_init(r_length, r_width);
}

bool shape_container_calc_rect(uint16_t r_length, uint16_t r_width)
{
    rectangle_init(r_length, r_width);
    rectangle_get_area();
    rectangle_get_perimeter();
    return rectangle_is_square();
}

Write the first test

Now that we have testable code, we want to write a test to make sure it works as we expect. To test the shape_conductor_init function, we’ll need to mock out that call to rectangle_init. To do that, we’ll use CMock.

In a new file called test_shape_container, we add our imports.


#include "unity.h" // The testing framework

#include "shape_container.h" // The header for the code we are testing
#include "mock_rectangle.h" // A mock header

The last header is the most interesting one. I have header.h  in my codebase, but currently mock_header.h does not exist. But once I run my test suite, CMock will generate that header, as well as a full mock_rectangle.c that I can use in my tests.

Next, we want to write our actual test.


void test_shape_container_init(void)
{
    // Set up known values
    uint16_t length = 4;
    uint16_t width = 3;

    //State, in order of call, what expectations we have, and the expected values to be returned, if any
    rectangle_init_Expect(length, width);

    //Run Actual Function Under Test
    shape_container_init(length, width);
}

Here we see CMock’s Expectations in action. When this test runs, Unity will expect shape_container_init to call rectangle_init with the specified values. If this does not happen, the test will fail.

To run this test, I run ceedling from the root of the project directory and get this output.


~ ceedling


Test 'test_shape_container.c'
-----------------------------
Compiling shape_container.c...
Linking test_shape_container.out...
Running test_shape_container.out...

--------------------
OVERALL TEST SUMMARY
--------------------
TESTED:  1
PASSED:  1
FAILED:  0
IGNORED: 0

The test passes! But that is an easy one. What if we want to verify that multiple functions are called? Or that the shape_container returns the correct one once it’s received data from the rectangle?

We can test that, too! Let’s write a test for shape_container_calc_rect and verify that it returns the same value that rectangle_is_square returns.


void test_shape_container_calc_rect_is_square(void)
{
    // Set up known values
    uint16_t length = 4;
    uint16_t width = 4;
    uint16_t x_area = length * width;
    uint16_t x_perimeter = length + width + length + width;
    bool x_is_square = true;

    //State, in order of call, what expectations we have, and the expected values to be returned, if any
    rectangle_init_Expect(length, width);
    rectangle_get_area_ExpectAndReturn(x_area);
    rectangle_get_perimeter_ExpectAndReturn(x_perimeter);
    rectangle_is_square_ExpectAndReturn(x_is_square);

    //Run Actual Function Under Test
    bool is_square = shape_container_calc_rect(length, width);

    //We can still verify whatever things we normally would after 
    TEST_ASSERT_EQUAL(x_is_square, is_square);
}

void test_shape_container_calc_rect_is_not_square(void)
{
    // Set up known values
    uint16_t length = 4;
    uint16_t width = 3;
    uint16_t x_area = length * width;
    uint16_t x_perimeter = length + width + length + width;
    bool x_is_square = false;

    //State, in order of call, what expectations we have, and the expected values to be returned, if any
    rectangle_init_Expect(length, width);
    rectangle_get_area_ExpectAndReturn(x_area);
    rectangle_get_perimeter_ExpectAndReturn(x_perimeter);
    rectangle_is_square_ExpectAndReturn(x_is_square);

    //Run Actual Function Under Test
    bool is_square = shape_container_calc_rect(length, width);

    //We can still verify whatever things we normally would after 
    TEST_ASSERT_EQUAL(x_is_square, is_square);
}

We’ve added two tests, one testing that the function returns true, and the other for returning false. In the test, we list our expectations in order that the functions will be called. Any other order results in a failing test. We also capture the output from our function under test and call a separate assertion on it to check its value.

Wrapping Up

Now you know everything you need to know to get started writing tests using CMock! This is just a fraction of the functionality that CMock provides. It is a powerful tool that can dramatically increase the speed at which you write unit tests in C. Now get out there and start mocking!

The code from this example can be found on my GitHub account.

Conversation
  • Alex says:

    shape_conductor_init -> shape_container_init
    It’s very confusing trying to understand typos in code, when unfamiliar with what is being generated automatically by a library.

    Thanks for your guide!

  • Mike Sortino says:

    Thank you for the CMock example. I have a dumb question: Your test just outputs the number of PASS/FAILS:

    OVERALL TEST SUMMARY
    ——————–
    TESTED: 4
    PASSED: 4
    FAILED: 0
    IGNORED: 0

    Run the rake command in test folder, I get this:
    TestCMock.c:21:test_MemNewWillReturnNullIfGivenIllegalSizes:PASS
    TestCMock.c:33:test_MemShouldProtectAgainstMemoryOverflow:PASS
    etc…
    How do I force the itemized output?

  • Comments are closed.