Setting up Unity test framework for CMake
Unity is a test framework written in C, which is commonly used for unit testing embedded software. It can be integrated with CMake, but official support is “a work in progress and not suitable for use yet”. However, as shown by this blog post from November 2022 and its corresponding example project, Unity can integrate well with CMake.
It’s been two years since that blog post was published, and both Unity and CMake have changed a little, so I decided to share the steps I took today in November 2024. The main differences between my steps and the aforementioned are:
- Fixing a minor issue (that arose due to changes in Unity).
- Updating the usage of CMake (due to changes in CMake).
- Aligning some things with my personal preference.
Project structure §
Following is how I have structured my project:
From top to bottom, the folders are:
submodules/external/unity_cmake
: The Unity test framework, included as a Git submodule, with a basicCMakeLists.txt
to build it as a library.dumb_example
: An example module (library) namedDumbExample
.app
: An application, which contains themain()
function, and uses theDumpExample
library.test
: A suite of tests, which test theDumbExample
library, but can easily be extended to test more modules as they are added.
Including Unity as a Git Submodule §
Add the submodule §
Use this command to add Unity as a submodule:
This includes the entire Unity repo at submodules/external/unity_cmake/Unity
.
Strictly speaking, to use Unity you only need to include these three files:
unity.c
unity.h
unity_internals.h
However, I chose to include the entire Unity Git repo as a submodule so that we have traceability and can easily upgrade to new releases. I also prefer to keep third-party code stored separately when possible, so that there is a clear division in ownership, hence the use of an external/
directory.
Add a CMakeLists.txt
§
We must provide a CMakeLists.txt
which builds Unity as a static library.
Create a CMakeLists.txt
in unity_cmake/
:
This provides Unity
as a library, which we use when building the tests.
DumbExample §
The files DumbExample.c
and DumbExample.h
come from the Unity docs on “Getting Started” (no anchor tag so you’ll have to scroll down quite far). They are quite simple:
The CMakeLists.txt
builds this as a library called DumbExample_lib
:
Application §
The application is not needed for testing purposes, but to provide a complete example, this is where one would fit into the picture:
To build the application:
Suite of tests §
For this example, we will only add tests for one module: DumbExample_lib
. In a real project, there would likely be multiple modules, and a suite of tests per module, each in their own folder.
Tests for DumbExample_lib
§
The test/dumb_example/
directory contains the suite of tests for DumbExample_lib
. Populate TestDumbExample.c
with some test code to verify DumbExample_lib
:
A key difference between this and the aforementioned blog post is the inclusion of setUp()
and tearDown()
.1
The CMakeLists.txt
for building these tests works by linking against DumbExample_lib
and Unity
:
In add_test
, NAME
argument is “simply a string name for the test […] that will be displayed by testing programs”. The COMMAND
argument is “the executable to run”.
Build all tests §
The file test/CMakeLists.txt
lists all test suites. For now, we only have one:
Top-level CMakeLists.txt
§
We use CTest to run the tests, which is an executable provided by CMake. This is required for the add_test()
command from earlier.
We use a top-level CMakeLists.txt
which:
- Defines the project metadata.
- Adds CTest.
- Builds the library under test.
- Builds the application.
- Builds Unity, the test framework.
- Builds all the tests.
Pulling it all together:2
Building §
To build, from the project root:3
Many older examples will show different ways of calling CMake, but this is the preferred modern way. Using cmake --build build
, instead of calling the specific make
or ninja
build tool directly, makes it simpler to follow the same build steps for different build systems.
The outputs will be in the build/
directory.
Running the tests §
Run CTest with:4
If all has gone well, you should see a passing test!
There is an open pull request to fix this on the example project. See also this SO answer. ↩︎
If you want to get a little more advanced, you can see this example which uses
TARGET_GROUP
to allow building the application or the tests. I decided to keep it simple for this example. ↩︎If you added the
TARGET_GROUP
option, then you would build the tests withcmake -DTARGET_GROUP=test -S . -B build
. ↩︎Using
--test-dir
tells CTest to that directory. If you callctest
from the wrong directory, you get the errorNo test configuration file found!
. This will happen if you runctest
without arguments in the project root. The solution is to either use--test-dir <dir>
to specify the build directory, or to ensure you runctest
from inside the build directory by doingcd <dir> && ctest
. In my view the--test-dir
option is preferable. ↩︎