Unit testing

This document is a user guide for adding unit tests to your C and C++ functions.

Using the gtest EPICS module

gtest is an EPICS module that enables you to use GoogleTest as a framework for unit testing and mocking.

The GoogleTest framework is a well-known framework for writing unit tests in C++. You can test C functions using GoogleTest, but the tests definitions must be in C++.

Functions to test are in a library

Note

Read this section if your top is an IOC.

If the top you want to test already is a library / a support top, you can skip this section.

To test your C/C++ functions, it’s best to have a library containing all the functions you want to test. This makes it easier to have your functions both in your IOC and in your tests.

For example, if you have a myFunctions.cpp, don’t add it to the sources of your IOC. Instead, create a library, for example myLibrary, and add your myFunctions.cpp to the sources of this library.

Then, link your IOC to myLibrary.

If you don’t have a library, change your src/Makefile so that it has these lines:

src/Makefile: building a library
LIBRARY += myLibrary
myLibrary_SRCS += myFunctions
# Libraries needed by 'myLibrary'
myLibrary_LIBS += Com

Then make sure your IOC links with myLibrary:

src/Makefile: linking an IOC to a library
# ...

#=============================
# Build the IOC application

PROD_IOC += myIoc

# ...

# Link myIoc with myLibrary
myIoc_LIBS += myLibrary_LIBS

Adding gtest to the top

Add gtest to the build environment:

flake.nix: adding the gtest support module
        # Add one of the supported modules here:
        # ---
        support.modules = with pkgs.epnix.support; [
          # other support modules...
          gtest
        ];

Writing tests

To write your unit tests, create a separate C++ file, and follow the GoogleTest documentation.

Here is an example of a test:

myTest.cpp: example gtest test definition
#include <gtest/gtest.h>

#include "myLibrary.hpp"

namespace {
class MyTest : public testing::Test {
protected:
    MyTest() { /* Set-up work for each test */ }
    ~MyTest() { /* Clean up work for each test */ }
};

TEST_F(MyTest, myTest1)
{
    // Test something
    EXPECT_EQ(2 + 2, 4) << "2 * 2 must be equal to 4";
}

TEST_F(MyTest, myTest2)
{
    EXPECT_EQ(6 * 7, 42) << "6 * 7 must be equal to 42";
}

} // namespace

Adding tests to the build

To add your tests to the build, add these lines to your src/Makefile:

src/Makefile: adding gtest tests
GTESTPROD_HOST += myTest
myTest_SRCS += myTest.cpp
myTest_LIBS += myLibrary
GTESTS += myTest

Running the tests

To run the tests in your development shell, run:

make runtests

Running nix build also runs tests by default.

To turn off tests inside the Nix build, add this to your configuration:

flake.nix: turning tests off
        # ...
        # checks.imports = [./checks/simple.nix];

        buildConfig.attrs.doCheck = false;

Using the epics-base facility

epics-base provides the epicsUnitTest.h facility for declaring unit tests, which can be useful if you don’t want to import an external EPICS module.

epicsUnitTest.h is made in C, but tests can be written in either C or C++.

Pre-requisites

Examine gtest’s Functions to test are in a library.

Writing tests

To write epics-base unit tests, make sure:

  • You include epicsUnitTest.h

  • You include testMain.h and use the MAIN macro to define your main function

  • Your main function starts with a testPlan(n), with n being the number of checks that your test will run

  • Your main function returns testDone()

  • You’re using testOk, testPass, and testFail to add checks

Here is an example test:

myTest.cpp: example epics-base test definition
#include <epicsUnitTest.h>
#include <testMain.h>

#include "myLibrary.hpp"

static void succeed()
{
    testPass("No issues succeeding");
}

static void checkAddition(int x)
{
    testOk(x + x == 2 * x, "x + x must be equal to 2 * x");
    testOk(x + x + x == 3 * x, "x + x + x must be equal to 3 * x");
}

MAIN(myTest)
{
    // 1 for succeed,
    // 2 for checkAddition which is called 5 times
    testPlan(1 + 5 * 2);

    succeed();

    for(int i = 0; i < 5; ++i) {
       checkAddition(i);
    }

    return testDone();
}

Adding tests to the build

To add your tests to the build, add these lines to your src/Makefile:

src/Makefile: adding epics-base tests
TESTPROD_HOST += myTest
myTest_SRCS += myTest.cpp
myTest_LIBS += myLibrary
TESTSCRIPTS_HOST += myTest.t

Running the tests

Examine gtest’s Running the tests.