Search code examples
c++unit-testingcmakedoctest

Dedicated main() for doctest.h but with tests also written in production code in C++


I'm under the impression this functionality exists on doctest but I wasn't able to implement it, and I'm not even sure anymore if this is possible.

I already have read a lot of references, but failed to understand correctly if that was my case or not:

With that being said, here's my slim down source tree:

.
├── CMakeLists.txt
├── include
│   └── cloysterhpc
│       ├── cloyster.h
│       ├── inifile.h
│       ├── mailsystem
│       │   └── postfix.h
│       └── network.h
├── src
│   ├── CMakeLists.txt
│   ├── inifile.cpp
│   ├── mailsystem
│   │   └── postfix.cpp
│   ├── main.cpp
│   └── network.cpp
└── test
    ├── CMakeLists.txt
    ├── inifile.cpp
    ├── main.cpp
    ├── network.cpp
    └── sample
        └── inifile.ini

The project structure is based on this cmake_template.

What I have now working:

  1. I can write the tests on separate files on ./test/file.cpp.
  2. Linking seems to be working correctly because I link the standalone test files with a static library generated from my project files.
  3. ./test/main.cpp is standalone and working.

What isn't working:

  1. Written tests in the production code does not appear on the standalone test executable.

To achieve this here's my snippets from the files:

./CMakeLists.txt

add_subdirectory(src)
include(CTest)
add_subdirectory(test)

./src/CMakeLists.txt

# Build a library and a main binary that links to the library
add_library(cloysterhpc_object OBJECT ${SOURCE_FILES})
add_library(cloysterhpc_static STATIC $<TARGET_OBJECTS:cloysterhpc_object>)
add_executable(main main.cpp)

target_link_libraries(
  main
  PRIVATE
    cloysterhpc_static
    cloysterhpc::cloysterhpc_options
    cloysterhpc::cloysterhpc_warnings)

target_link_system_libraries(
  main
  PRIVATE
    ...
    doctest::doctest)

target_link_libraries(
  cloysterhpc_object
  PRIVATE
    cloysterhpc::cloysterhpc_options
    cloysterhpc::cloysterhpc_warnings)

target_link_system_libraries(
  cloysterhpc_object
  PRIVATE
    ...
    doctest::doctest)

target_include_directories(cloysterhpc_object PRIVATE "${CMAKE_BINARY_DIR}/configured_files/include")
target_include_directories(cloysterhpc_object ${WARNING_GUARD} PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>)

target_include_directories(main PRIVATE "${CMAKE_BINARY_DIR}/configured_files/include")
target_include_directories(main ${WARNING_GUARD} PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>)

./test/main.cpp

#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>

// This is all that is needed to compile a test-runner executable.

./test/CMakeLists.txt

file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
message(STATUS "Test files found are: ${TEST_SOURCES}")

add_executable(${PROJECT_NAME} ${TEST_SOURCES})

target_link_libraries(
  ${PROJECT_NAME}
  PRIVATE
    cloysterhpc_static
    cloysterhpc::cloysterhpc_warnings
    cloysterhpc::cloysterhpc_options)

target_link_system_libraries(
  ${PROJECT_NAME}
  PRIVATE
    ...
    doctest::doctest)

target_include_directories(${PROJECT_NAME} ${WARNING_GUARD} PRIVATE
        $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/../include>
        $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/../include>)

doctest_discover_tests(${PROJECT_NAME})

If I write any test in any .cpp file inside ./test everything works fine. But if the test is located in source file from ./src directory it will not be included in the test executable.

For the tests on ./src I've added that as following, at the end of the file:

./src/inifile.cpp

#define DOCTEST_CONFIG_IMPLEMENT
#include <doctest/doctest.h>

TEST_SUITE("Load files")
{

    TEST_CASE("Get information")
    {
        inifile ini;
        std::filesystem::path path;
        path = std::filesystem::current_path() / "sample/inifile.ini";
        ini.loadFile(path);
        std::string clusterName = ini.getValue("information", "cluster_name");
        CHECK(clusterName == "cloyster");
    }
}

My goal was to be able to have:

  • Tests written on production code to do unit testings.
  • Support for tests on separate files to achieve a more complex scenario.
  • Every test compiled to the same separate binary, on the ./test directory.
  • Keep my main function intact
  • Be able to continue with a simples separate main.cpp file for tests, that uses #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN.

I've added only snippets of the code I thought was relevant, but at this stage I'm not even sure what is relevant or not. The full code can be found on my repo if anyone if willing to look at it: https://github.com/viniciusferrao/cloysterhpc

If there's more information need please let me know, and if it isn't possible what are the possible solution to the issue?

Thanks in advance.


Solution

  • I was able to solve the issue recompiling every object two times. Yes I know that will increase the compilation time but it's a price that I'm willing to pay.

    With that I just added the following to my .cpp files:

    #ifdef BUILD_TESTING
    #include <doctest/doctest.h>
    #else
    #define DOCTEST_CONFIG_DISABLE
    #include <doctest/doctest.h>
    #endif
    

    So if BUILD_TESTING is enabled during compile time it will not enable DOCTEST_CONFIG_DISABLE.

    Doing that I was able to achieve what I want, that was still keep the tests on the source files while maintaining a totally separate main.cpp file and support for additional separate test files.