Skip to content

Ceedling Tutorial / Kickstart Guide

This guide provides a kickstart for using Ceedling for unit testing in this repository.

Video version

Video tutorial (nakaz)

1. What is Ceedling?

Ceedling is a build system focused on test-driven development in C. It integrates other open-source tools:

  • Unity: A unit testing framework for C.
  • CMock: A tool for creating mock objects from C header files.
  • CException: A simple exception handling framework.

It automates the process of creating and running tests, making it easier to write and maintain unit tests for C projects, especially in an embedded context.

2. Installation

Ceedling is a Ruby gem, so you need to have Ruby installed.

2.1. Install Ruby

  • Windows: Download and install Ruby from rubyinstaller.org. It is recommended to use a version like 3.0.x, as newer versions might have compatibility issues. Make sure to add Ruby to your PATH.
  • Linux: Use your distribution's package manager. For example, on Debian/Ubuntu: sudo apt-get install ruby

2.2. Install Ceedling

Once Ruby is installed, open a terminal and run:

bash
gem install ceedling

This will install Ceedling and its dependencies.

2.3. Verify Installation

You can verify the installation by running:

bash
ceedling version

3. Creating a Ceedling Project

To create a new Ceedling project, navigate to the directory where you want to create your project and run:

bash
ceedling new <project_name> <directory>

This will create a new directory with the specified name, containing the basic Ceedling project structure:

  • project.yml: The main configuration file for your project.
  • src/: You can delete this directory, we use the CubeMX Src directory.
  • test/: Directory for your test files.
  • build/vendor/: Contains the source for Ceedling's dependencies like Unity and CMock.

For our workflow, just run this command in the workspace root, where CMakeLists.txt also resides:

bash
ceedling new .

4. Writing a Test

Let's say you have a module calculator with a header file src/calculator.h and a source file src/calculator.c.

src/calculator.h:

c
#ifndef CALCULATOR_H
#define CALCULATOR_H

int add(int a, int b);

#endif // CALCULATOR_H

src/calculator.c:

c
#include "calculator.h"

int add(int a, int b) {
    return a + b;
}

To test the add function, you would create a test file test/test_calculator.c:

test/test_calculator.c:

c
#include "unity.h"
#include "calculator.h"

void setUp(void) {
    // Set up code, executed before each test
}

void tearDown(void) {
    // Tear down code, executed after each test
}

void test_add_should_returnTheSumOfTwoIntegers(void) {
    // Test case
    TEST_ASSERT_EQUAL(5, add(2, 3));
}
  • #include "unity.h": Includes the Unity test framework.
  • #include "calculator.h": Includes the header of the module under test.
  • setUp and tearDown: Functions that are run before and after each test.
  • test_...: Test functions must start with test_.
  • TEST_ASSERT_EQUAL: A Unity assertion macro. The test passes if the two arguments are equal.

5. Mocking with CMock

CMock allows you to "mock" dependencies. Imagine your calculator now uses a hardware abstraction layer (HAL) to get a value.

src/adder_hal.h:

c
#ifndef ADDER_HAL_H
#define ADDER_HAL_H

#include <stdint.h>

uint16_t adder_hal_GetValue(void);

#endif // ADDER_HAL_H

src/calculator.c:

c
#include "calculator.h"
#include "adder_hal.h"

uint16_t calculator_AddBaseValue(uint16_t input_value)
{
    uint16_t base_value = adder_hal_GetValue();
    return input_value + base_value;
}

To test calculator_AddBaseValue without calling the actual HAL function, you can mock adder_hal.h.

test/test_calculator.c:

c
#include "unity.h"
#include "calculator.h"
#include "mock_adder_hal.h" // Include the mock header

void test_calculator_AddBaseValue_should_AddCorrectly(void) {
    // Expect the mock function to be called and return 10
    adder_hal_GetValue_ExpectAndReturn(10);

    // Call the function under test
    uint16_t result = calculator_AddBaseValue(5);

    // Assert the result
    TEST_ASSERT_EQUAL(15, result);
}
  • #include "mock_adder_hal.h": By including this file (which doesn't exist yet), you are telling Ceedling to create a mock for adder_hal.h.
  • adder_hal_GetValue_ExpectAndReturn(10): This is a CMock expectation. It tells the mock to expect a call to adder_hal_GetValue and, when called, to return 10.

6. Running Tests

To run all tests, navigate to the root of your Ceedling project and run:

bash
ceedling test:all

Ceedling will then build and run all your tests and provide a summary of the results.

7. VSCode extension

The standard Ceedling extension works for Ceedling 1.0.0+, but the debugging feature is broken, so I've used a fork of said extension. I had to manually compile it.

To install it, go to extensions, click on the three dots and select Install from VSIX. Choose this file.

VSCode Extension tutorial image

Released under the MIT License.