Search code examples
c++unit-testingcmakecatch-unit-test

How do I make my cmake targets for catch2 testing and project running more extendable and sensible?


After a lot of hard work and research, I've managed to make multiple cmake targets to separate running my program from running tests on the code. But I don't like what I've made because I see redundancy in the CMakeList.txt files.

Currently I have to add every new source file to both targets so that the source target can build using that file and the test target can build since they need to test that file. I can't throw the entire source target into the test target because then the test target would contain two main files.

My only idea of how to fix the redundancy is to put all of the files in the source target without the main.cpp file into some group, and then attach that group to both targets. That way the source target contains just the main.cpp file and the source file group, and the test target contains all its tests and the source file group. So the file group would basically be all of the overlap between the two targets. I just don't know how to do that.

Research

Here's the other stack overflow questions I found that helped me get to where I am:

Code

Here is my test project I made to experiment with catch2 and cmake that currently works for both build targets "tests" and "catch2Test":

/catch2Test      // <-- project folder
  |---- /include
  |       |---- /catch2
  |               |---- catch.hpp
  |---- /src
  |       |---- /myMath
  |       |       |---- factorial.cpp
  |       |       |---- factorial.h
  |       |---- main.cpp
  |       |---- CMakeLists.txt
  |---- /test
  |       |---- test_main.cpp
  |       |---- test_factorial.cpp
  |       |---- CMakeLists.txt
  |---- CMakeLists.txt

/include/catch2/catch.hpp is the library header file for catch2

/src/myMath/ contains the header file and code file for the factorial implementation, same as used in the catch2 tutorial. This is also where the implementation for the factorial tests come from.

/src/main.cpp is a simple main file that includes factorial.h to do factorial math and then prints it to cout.

Here's explicit code for the remaining files that actually matter:

/test/test_main.cpp

#define CATCH_CONFIG_MAIN
#include "../include/catch2/catch.hpp"

/test/test_factorial.cpp

#include "../include/catch2/catch.hpp"
#include "../src/myMath/factorial.h"

TEST_CASE("Factorials are computed", "[factorial]")
{
    REQUIRE(factorial(0) == 1);
    REQUIRE(factorial(1) == 1);
    REQUIRE(factorial(2) == 2);
    REQUIRE(factorial(3) == 6);
    REQUIRE(factorial(10) == 3628800);
}

/CmakeLists.txt

cmake_minimum_required(VERSION 3.13)

set(CMAKE_CXX_STANDARD 14)

add_subdirectory(src)
add_subdirectory(test)

/test/CMakeLists.txt

add_executable(tests
        ../src/myMath/factorial.cpp ../src/myMath/factorial.h

        ../include/catch2/catch.hpp

        test_main.cpp
        test_factorial.cpp
)

/src/CMakeLists.txt

add_executable(catch2Test
        main.cpp
        myMath/factorial.cpp myMath/factorial.h
)

Running

When I run the catch2Test target, I get desired results:

Hello, World!
Factorial 0! = 1
Factorial 1! = 1
Factorial 2! = 2
Factorial 3! = 6
Factorial 6! = 720
Factorial 10! = 3628800

Process finished with exit code 0

And when I run the tests target, I also get desired results:

===============================================================================
All tests passed (5 assertions in 1 test case)


Process finished with exit code 0

It all works, I just don't like my current solution.

How can I make my targets more easily extendable?

Also, side question: what's the more appropriate way to include a header library? I assume it's not supposed to be in the add_executable() like I did in /test/CMakeLists.txt as ../include/catch2/catch.hpp...


Solution

  • I think I found an answer to my question in the list of related questions in the sidebar: cmake project structure with unit tests

    Using add_library I can create a list of files with a new target name that doesn't necessarily contain a main file. Here's how I've changed my subdirectory CMakeList.txt files:

    src/CMakeList.txt

    add_library(src
            myMath/factorial.cpp myMath/factorial.h
    )
    
    add_executable(catch2Test main.cpp)
    
    target_link_libraries(catch2Test src)
    

    test/CMakeList.txt

    add_executable(tests
            ../include/catch2/catch.hpp
    
            test_main.cpp
            test_factorial.cpp
    )
    
    target_link_libraries(tests src)
    

    Now all my source files that I add go into the src library and both of my targets link to it using target_link_libraries([target] src).

    Now I'm trying to figure out if I can use target_link_libraries to attach the catch2 lib to the tests target.

    Edit: I think I found my answer to my second question here: 'cmake os x failure ar no archive members specific' and here: 'how to create a cmake header only library that depends on external header files'.

    I tried to make a library out of a single header file and cmake doesn't recognize header files apparently. I now have a CMakeLists.txt in my /include folder that looks like:

    add_library(catch2 INTERFACE)
    target_sources(catch2 INTERFACE catch2/catch.hpp)
    

    I've added the /include folder to the list of added subdirectories in my main CMakeLists.txt file.

    Also my tests target now links the catch2 library by using target_link_libraries(tests catch2) which I find much nicer than putting a strained directory link inside the list of executable files for the tests target.