Unit testing has become an essential part of developing high-quality, reliable and maintainable software, but is not as commonplace in the firmware industry as it ought to be. If you are tired of being slowed down by the “build-flash-debug” style of development, waiting for hardware or having to share hardware with several other developers, then off-target unit testing and test-driven development is for you!
There are several C and C++ unit testing frameworks available, but I have personally had the best experience with the GoogleTest framework from Google. It is fairly easy to set up, is well-documented, works with both C and C++, and you can get a plugin for Eclipse that gives you a nice little tree view of your test suite – and of course the green bar of success when all your tests pass.
I mostly work with STM32 microcontrollers and use STM32CubeIDE for development, which I assume you already have installed. Unfortunately, setting up a unit testing environment in STM32CubeIDE on Windows is not a simple one-click operation, but requires a few steps. In this tutorial I will walk you through:
- Installing Cygwin (and the packages required for compilation)
- Compiling GoogleTest from source
- Creating a unit testing project in STM32CubeIDE.
- Running your tests with the C/C++ Unit Testing Support plugin.
Installing Cygwin
Cygwin is a Linux-like environment for Windows, which we will be using to compile both the GoogleTest (and GoogleMock) source code and your own source code using the the GNU C/C++ Compilers (gcc and g++, respectively). We will be using the 32-bit version of Cygwin, since we are going to be testing code intended to run on a 32-bit microcontroller. Download the x86 installer from the Cygwin website.
When you get to the Select Packages page during installation, select Full in the View dropdown menu to show all packages. Then select the following packages for installation by selecting the latest version in the dropdown menu where it says “Skip”:
- gcc-core
- gcc-g++
- gdb (choose v9.2-1, newer versions do not work with Eclipse)
- binutils
- make
- cmake
- python3
Installing GoogleTest
Download the newest release of GoogleTest (v. 1.11.0 at the moment of writing this) from the Github repository. Unpack the zip-archive. Then open a Cygwin Terminal, browse to the download location and compile the library, e.g.:
$ cd /cygdrive/c/Users/Klein/Downloads/googletest-release-1.11.0
$ mkdir build && cd build
$ cmake ..
$ make
If everything goes smoothly, the library files should now be located in the lib
folder within the current working directory, while the header files are located in the googletest/include
and googlemock/include
folders in the parent directory.
To install both header and library files to the Cygwin directory (i.e. C:\cygwin\usr\local\include
and C:\cygwin\usr\local\lib
), also run:
$ make install
In STM32CubeIDE, the gcc/g++ compiler should be able to find the header files automatically, but it might be necessary to copy/move the libraries from C:\cygwin\usr\local\lib
to C:\cygwin\usr\i686-pc-cygwin\lib
in order for the linker to find them.
Creating your first unit test project
Open up STM32CubeIDE and create a new C/C++ Project using the C/C++ Managed Build template. For the project type, select Executable > Empty Project, and select Cygwin GCC as the toolchain. Choose a name for your project and click Finish.
Now let’s add the GoogleTest library to the project. Right-click on your project in the Project Explorer and select Properties. Browse to C/C++ General > Paths and Symbols and open the Libraries tab. Now add the following libraries:
- gtest
- gtest_main
- pthread (optional on Windows, required on Linux)
You can choose Add to all configurations if you intend to use both Debug and Release build configurations.
The gtest
library contains the GoogleTest functionality itself. The gtest_main
library is optional (but recommended) and contains a main()
function that runs all tests in your project. This means you do not have to implement a main()
function yourself – only the tests.
Before creating our first test, lets make a folder for our test sources by right-clicking the project and selecting New > Source Folder and naming it “test”. Inside the test folder create a C++ source file, e.g. MyFirstTest.cpp
.
Now, we will include the gtest.h
header and use the TEST
macro to create two example tests, one that passes and one that fails:
#include <gtest/gtest.h>
TEST(MyFirstTestClass, ThisTestPasses)
{
SUCCEED();
}
TEST(MyFirstTestClass, ThisTestFails)
{
FAIL();
}
Try building the project (Ctrl + B) to ensure that everything works.
Running the tests
By now you can simply run you program as a C/C++ Application and watch the test output in the console. This is probably the way you want to do it while coding, so you can quickly press F10 to run your tests.
There is, however, a C/C++ Unit Testing Support plugin available for Eclipse, that lets you view the result of all your tests in a separate view. This really comes in handy as your test suite grows. The only downside is that you have to use your mouse to press the “Rerun All Tests” button, in order to display the test output in this view.
Go to Help > Install New Software and select the Eclipse repository in the “Work with” dropdown menu. Now search for “unit testing” and you should see C/C++ Unit Testing Support. Check the box and click finish. After the plugin is installed you will be prompted to restart the IDE.
After restarting, go to Run > Run Configurations and create a new C/C++ Unit configuration. Under the C/C++ Testing tab, select Google Tests Runner, click Apply and then Run.
A new C/C++ Unit view should appear and show your test results:

Let’s modify the failing test to get both tests to pass and get that sweet green bar of success:
#include <gtest/gtest.h>
TEST(MyFirstTestClass, ThisTestPasses)
{
SUCCEED();
}
TEST(MyFirstTestClass, ThisTestAlsoPasses)
{
int a = 4, b = 6;
int sum = a + b;
ASSERT_EQ(sum, 10);
}
Here I have used the ASSERT_EQ
macro which tests if two variables are equal. If my calculations are correct, both tests should now pass when rerunning the program:

Congratulations! You are now ready to start unit testing.
Further reading
To learn more about GoogleTest, I recommend reading the GoogleTest Primer in the GoogleTest User’s Guide.
To learn more about unit testing and test-driven development specifically for embedded systems, I recommend reading “Test Driven Development for Embedded C” by James W. Grenning.
Thanks for this post/guide – it has been helpful to get started! One thing that I am struggling with is how to actually integrate this with an embedded firmware project and structure the project. I would like the tests just to live in another folder called Tests — so with a standard STM32CubeIDE project structure we would have source code in Drivers, Core, and Tests directories.
Unfortunately it seems that Eclipse cannot exclude source files from individual build configurations – only from all of them. So I cannot have Tests in the same project. So now I need two projects, but projects can’t be in the same directory or nested so I can’t actually put the Tests folder where intended. Any ideas on solutions?
Seems like the eclipse/stmcubeide restrictions are preventing tests from being setup in a relatively standard way that is easy to do in other IDEs.
Hi Jonathan!
I agree that CubeIDE does not exactly make it easy to set up. On my setup (STM32CubeIDE 1.9.0) I have no problem excluding files and/or folders from specific build configurations. When right-clicking a folder in the Project Explorer I select “Resource Configurations > Exclude from Build” and can then select which build configurations it should be excluded from. However, I have not found a good way to use different compilers for each build configuration in a single project. Ideally I wanted the “Debug/Release” configurations to use arm-none-eabi-gcc and then have a “Test” configuration to use the host GCC, in order to run the tests off-target. If you figure out a neat way to do this, please let me know.
The way we have been doing it (up until recently) is, we have our main STM32 Project containing the actual application and then a separate C/C++ Project (Cygwin GCC) for the tests. In the test project we would link to folders in the application project that contain modules we wish to test. You do this in CubeIDE by creating a new folder in the test project, clicking “Advanced” in the New Folder dialog, check “Link to alternate location (Linked folder)” and selecting the desired folder in the application project. This way we can edit the application code directly from the test project. We of course had the two projects in the same Git repository to keep the tests and application code in sync.
Recently we have been considering moving away from STM32CubeIDE altogether and using VS Code and CMake instead. As a first step in this transition, I moved our test code to a “Tests” folder in the application project (same as you describe) and added a “CMakeLists.txt” to include sources/headers from the application project. This folder is excluded from all builds in the STM32 project and we then run the tests from VS Code instead. We plan to also convert our application project to CMake eventually so we have everything in VS Code, but if you prefer to keep using STM32CubeIDE, having just the test project in VS Code is also feasible. Using CMake requires a bit more maintenance than having CubeIDE do everything for you, but I think it is worth the effort to gain more control over the build process. It also makes it easier to have our Jenkins server create builds for many different targets/hardware configurations.
Thanks for the reply!
I have actually managed to get this set-up all in one STM32CubeIDE project. Critical setting I hadn’t found was C/C++ general -> Paths & Symbols -> Source Location. By default this recursively includes everything in the project, so I had to manually add the all the non-Test top level folders to the Release/Debug configurations.
I also was not able to create the Test configuration in the same project, however I found a bit of a work around. I followed your full guide to set-up a test only project as a sort of “donor”. Then with both projects open I created a new build config, “Test”, in the main project and had it copy the settings from the “donor” project build config (Build Configurations -> Manage -> New -> Import from projects). I then had to tweak a few settings, such as the above source location, that are project specific but all the build settings with the different compiler copied over.
So right now everything seems to be working locally for me, although it seems a bit fragile. Getting these tests to run in CI (probably GitHub actions) is a future goal so moving to CMake is probably the right direction for that. But at least right now I can get started with creating tests! Thanks!
That’s a clever little workaround. I’ll try that out as well – thanks for sharing!
If you are not already aware, there is a shell script in the STM32CubeIDE folder (“headless-build.bat” on Windows) that let’s you build your project (with a specified build configuration) from the command-line. We’re using that currently on our Jenkins server. It is simple to use, but of course you need to install CubeIDE on the build server. You could consider this as a temporary solution before moving to CMake.
I’m glad my post could get you started – good luck 🙂