An Engineer’s Guide to Unit Testing – CMake And Boost Unit Tests

All programmers have heard of unit testing. Countless blogs discuss the importance of unit testing and how all projects should use it. Paragraph after paragraph of abstract discussion, yet no concrete examples. This post is for engineers, a step by step, 5 minute guide on setting up Boost Unit Tests with a CMake project.

We’re going to use Boost’s Unit Test Frame (UTF) (docs). I start with making modules for each class we wish to test by making one source file and using #define BOOST_TEST_MODULE ([1], [2]). I’m a fan of linking to boost, so I use BOOST_TEST_DYN_LINK at the top of my modules ([3]). Tests are simply functions. To generate and initialize a main function to call my tests and monitor their status, I use the BOOST_AUTO_TEST_CASE approach ([4], [5]). Finally, you need to write the assertions/value tests within each function – I like to use BOOST_CHECK BOOST_CHECK_EQUAL BOOST_CHECK_MESSAGE ( [6]). Below we put these four concepts into one easy test file.

test_SomeBaseClass.cpp

//Link to Boost
 #define BOOST_TEST_DYN_LINK

//Define our Module name (prints at testing)
 #define BOOST_TEST_MODULE "BaseClassModule"

//VERY IMPORTANT - include this last
#include <boost/test/unit_test.hpp>

#include "some_project/some_base_class.h"
// ------------- Tests Follow --------------
BOOST_AUTO_TEST_CASE( constructors )
{
  SomeBaseClass obj;
  SomeBaseClass obj2;

//Check default constructor
//Use BOOST_CHECK for small equal checks - true or false
  BOOST_CHECK(obj.memb == obj2.memb);
  ......
}

//Name your test cases for what they test
BOOST_AUTO_TEST_CASE( assignment )
{
  SomeBaseClass obj;
  SomeBaseClass obj2;
  
  obj.setSomeValues.....
  
  obj2 = obj;

//Use BOOST_CHECK_EQUAL if you want the values to be 
//printed out if they mismatch
  BOOST_CHECK_EQUAL(obj2.getValue(), "SomeValue");
}

Now that we have a series of test sources, we can automate their compiling and running using CMake. We must enable testing in cmake, link to boost and libraries you use, compile the tests and add them to the cmake testing infrastructure. A little parsing is involved to fully automate the process (you can also see why I run one test suite per file) – the example below presents each stage.

These lines must exist in your CMakeLists.txt

#Setup CMake to run tests
enable_testing()

#Prep ourselves for compiling boost
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
include_directories (${Boost_INCLUDE_DIRS})

#I like to keep test files in a separate source directory called test
file(GLOB TEST_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} test/*.cpp)

#Run through each source
foreach(testSrc ${TEST_SRCS})
        #Extract the filename without an extension (NAME_WE)
        get_filename_component(testName ${testSrc} NAME_WE)

        #Add compile target
        add_executable(${testName} ${testSrc})

        #link to Boost libraries AND your targets and dependencies
        target_link_libraries(${testName} ${Boost_LIBRARIES} [YOUR TARGETS/LIBRARIES])

        #I like to move testing binaries into a testBin directory
        set_target_properties(${testName} PROPERTIES 
            RUNTIME_OUTPUT_DIRECTORY  ${CMAKE_CURRENT_SOURCE_DIR}/testBin)

        #Finally add it to test execution - 
        #Notice the WORKING_DIRECTORY and COMMAND
        add_test(NAME ${testName} 
                 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testBin 
                 COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/testBin/${testName} )
endforeach(testSrc)

Finally – we’re ready to test our project.

mkdir build
cd build
make

make test

There you have it, a full unit testing framework in the time it takes you to read this.

Posted in:
About the Author

Constantin Savtchenko

I attended Lehigh University, focusing on algorithms, and graduated in 2011 with a Masters in Computer Science. At Neya I have been part of a variety of projects all differing in domain. I have a wide range of programming experience, including parallel programming with GPU's.