Adding Unit Tests To Your Firmware Using Ceedling + GitHub Actions CI in Bluetooth Development

If you followed our last blog post Adding A CLI, we added a simple CLI to a project for the Nordic NRF52840-DK.

Well, as a best practice we want to add some unit tests. Just to make sure everything will work as expected, and see if we can find any issues with our implementation. In this post, we’ll show you exactly how we do it.

Unit Testing Toolchain: Ceedling & Unity

We like Ruby on Rails, so naturally we really like ceedling and unity. This is a unit test build management system installed via brew (if you are on macOS). Under the hood it’s using Unity.

If you need instructions on how to install ceedling and unity, follow this link.

Once installed cd out of your active directory and run ceedling new cli. However you don’t need to do this, because our repo is already set up.

Then I simply added a new module called cli by running:

ceedling module:create[cli]

which generates our test file automatically. Again, you don’t have to do this if you are using our repo, but I love how easy it is.

Now one thing to note with ceedling is that it expects to see a certain folder structure. It’s very similar to Ruby on Rails projects and is also just a good way to structure your repos. This is what that file structure looks like:

/build - contains the ceedling build

/examples - was already there, also a good place to keep your main.c, as it can cause issues with ceedling

/src - contains your source code files

/test - contains your test files for each module

So just be aware of this if you are adding ceedling to an existing project, it can be a bit of a pain to deal with initially.

Last but not least, just run ceedling test:all to run the unit test suite. And you see this:

Running & automating with GitHub Actions

Now, this is great if we were to run a test manually, but we need some automation. In fact, we’re BIG believers at Ovyl that the embedded systems/firmware community needs to continue to make the beleaguered movements towards emulating our software and web development compatriots. We do it every chance we can get!

Modern web development’s tooling for unit testing and test automation is phenomenal. Testing is a first class citizen. When dealing with firmware, we are happy just to get something to compile. So, let’s make the effort to automate our unit test suite.

Luckily this is actually fairly easy. GitHub Actions is an incredibly powerful tool, once you get the hang of the syntax it's great to work with.

Simply add a .github directory, and inside of that another directory named workflows. Inside of that add a file called tests.yml.

GitHub Actions File Location in Finder

GitHub Actions allow you to specify a docker container to use to run your unit tests, and there is a docker image preloaded with ceedling ready to go, offered by the makers.

Check out our file here: github link. We use our own custom docker image, but Ceedling's official could work for you.

Now every time you commit (can be branch dependent), it will run the test suite, you can check it out here:

Let’s see how our commit did:

Ahhh it didn’t work!

It looks like I tried to run gcov to get a coverage report without enabling it in the ceedling project.yml file.

Let’s run this locally to confirm (oops)...

Same error.

Let’s enable it, just add gcov here at the end of the project.yml file:

And now it works locally:

Once fixed, we have a nice indication it passed, with the git commit message:

You can even see console output:

Not bad.

I think we can definitely improve the test suite, but this is a nice guide on how to get it all setup.

Here’s some things we’re thinking about writing about next. Let us know what you’re interested in!

  1. Ceedling vs CppUTest
  2. Unit Testing Within a BLE Project
  3. Using GitHub Actions to Create and Store Firmware Binary Files

Nick Sinas
Jan 4, 2022
Nick Sinas
Or click here to learn more ->