3

I am trying to use Boost.Test (v1.69.0) with modern CMake (v3.15.0) to write and build my unit tests. The main difficulty is to split my tests in several test files: in this case Boost.Test cannot find tests.

I use the Boost distribution provided by my package manager on Linux (OpenSUSE), which means I have development headers and dynamic libraries.

Most of the documentation/tutorials/examples I found about Boost.Test/CMake interaction involves old versions of CMake (v2), and you know how much the syntax and philosophy of CMake evolved between v2 and v3. So what I found was not suited for my case.

Here is a simple MWE of my situation:

project
├── CMakeLists.txt
└── tests
    ├── test_one.cpp # first test file
    ├── test_two.cpp # second test file
    └── test_unit.cpp # main test file

The main test file, which does not contain actual testing:

// tests/test_unit.cpp
#define BOOST_TEST_MODULE "Unit Test"
#define BOOST_TEST_DYN_LINK // I use dynamic libraries of Boost
#include <boost/test/unit_test.hpp>

The first test file, which contains actual testing:

// tests/test_one.cpp
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(testOne)

BOOST_AUTO_TEST_CASE(testDummy) {
        BOOST_TEST(true);
}

BOOST_AUTO_TEST_SUITE_END() // testOne

The second test file is completely equivalent, so I will not show it. The CMake configuration:

# CMakeLists.txt
# cmake version
cmake_minimum_required(VERSION 3.9...3.15 FATAL_ERROR)

# project definition
project(Dummy VERSION 0.0.0 LANGUAGES CXX)

# external libraries
find_package(Boost COMPONENTS unit_test_framework REQUIRED)

# first testing library
add_library(test_one tests/test_one.cpp)

# second testing library
add_library(test_two tests/test_two.cpp)

# test executable
add_executable(test_unit tests/test_unit.cpp)
target_link_libraries(test_unit Boost::unit_test_framework test_one test_two)

# testing command
enable_testing()
add_test(test_unit test_unit)

So, I declare the two test files as libraries and I link them statically to the main test executable.

Now, I can build this MWE, but the binary test_unit fails to find tests and outputs:

Test setup error: test tree is empty

I do not understand why. I found two unsatisfactory ways to make it work:

  1. I declare the two libraries dynamic, e.g. add_library(test_two SHARED tests/test_two.cpp). But this makes little sense to me to use dynamic linking for something living in the same directory as the executable file.

  2. I include the two test files in the main test file and remove the libraries in CMakeLists.txt. But this is ugly.

Currently, I cannot use static linking for Boost.Test, as static libraries are not provided by my package manager. I tried to use header-only Boost.Test, but the problem is the same.

What should I do?

Neraste
  • 485
  • 4
  • 15
  • 2
    Do you really need a library per each test file? – babu646 Sep 03 '19 at 18:55
  • I've seen it done before, in a Real Project. I thought it was... excessive. My poor poor linker! – Mark Storer Sep 03 '19 at 20:04
  • IIRC, there was some blog about doing it that way... something to do with the way `boost_test` handles creating its own `main()`. If you write all your tests without specifying their main macro anywhere, then this will wrap them for you... but if you have a main you can stack N cpp files up in a single exe. – Mark Storer Sep 03 '19 at 20:37
  • @MarkStorer this is what `#include ` does with the various macro you may define before. – Neraste Sep 04 '19 at 01:06
  • @IanA.B.K. on a real project, it is better to have one test file per feature library that only tests this feature library. What other option should I have but to declare these test files as libraries in CMake? – Neraste Sep 04 '19 at 01:36
  • According to https://www.boost.org/doc/libs/1_66_0/libs/test/doc/html/boost_test/runtime_config/test_unit_filtering.html, you can filter your tests in a variety of ways (compile time and run time), no need to break them down to this level just for the control over execution. – Mark Storer Sep 04 '19 at 12:21

1 Answers1

2

As pointed out in the comments, each test file does not need to be a library. I actually only need the executable:

# test executable
add_executable(test_unit
    tests/test_unit.cpp
    tests/test_one.cpp
    tests/test_two.cpp
)
target_link_libraries(test_unit Boost::unit_test_framework)

On a side note, I can even factorize the #define BOOST_TEST_DYN_LINK in test files with:

target_compile_definitions(test_unit PUBLIC BOOST_TEST_DYN_LINK)

So everything works.

Neraste
  • 485
  • 4
  • 15