如何使用CMake对库(TDD)的私有功能进行单元测试

时间:2019-02-12 14:30:14

标签: c++ unit-testing testing cmake tdd

我想使用CMake设置项目,以便可以使用TDD进行开发,这意味着包括并测试内部标头。但是,使用CMake正确设置该库会在我的单元测试中隐藏这些实现细节(对于外部使用也是正确的)。

给出如下文件和文件夹结构:

Foo
|-- include
|   `-- Foo
|       `-- Foo.h
|-- CmakeLists.txt
|-- src
|   |-- Bar
|   |   |-- Bar.h
|   |   `-- Bar.cpp
|   |-- Baz
|   |   |-- Baz.h
|   |   `-- Baz.cpp
|   |-- Foo.cpp
|   `-- CMakeLists.txt
`-- test
    |-- Bar
    |   `-- BarTest.cpp
    |-- Baz
    |   `-- BazTest.cpp
    |-- FooTest.cpp
    `-- CMakeLists.txt

Foo / CMakeLists.txt

project(Foo)
include(CTest)
add_subdirectory(src)
if(BUILD_TESTING)
    add_subdirectory(test)
endif()

Foo / src / CMakeLists.txt

add_library(Foo)
target_include_directories(Foo
    PUBLIC
        $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
    PRIVATE
        ${CMAKE_CURRENT_LIST_DIR}
)

target_sources(Foo
    PRIVATE
        Foo.cpp
        Bar/Bar.cpp
        Baz/Baz.cpp
)

Foo / test / CMakeLists.txt

find_package(Catch2 REQUIRED)
include(Catch)

add_executable(FooTest
    FooTest.cpp
    Bar/BarTest.cpp # This file can't #include "Bar/Bar.h"
    Baz/BazTest.cpp # This file can't #include "Baz/Baz.h"
)

target_link_libraries(FooTest
    PRIVATE
        Foo
        Catch2::Catch2
)

现在的问题是,FooTest与Foo链接的方式与其他项目在需要Foo库作为依赖项时所采用的方式相同。这就产生了一个问题,我无法在包含文件Bar/BarTest.cppBaz/BazTest.cpp的{​​{1}}和#include "Bar/Bar.h"中运行单元测试。 在我看来,理想的解决方案是能够为Foo获取一个内部#include "Baz/Baz.h目标,该目标包括PUBLIC在内的所有内容,然后我可以对其进行测试。但是,安装后,内部标头是私有的,只有shortcut中的文件是公共的。

我看到的解决方案是创建许多子目标,并仅包含每次测试所需的内容。但这似乎很麻烦,并且在非常模块化的设置中不适用于像柯南这样的软件包管理器。

其他解决方案是创建一个重复的Foo目标,其中所有内容都是公共的,但这需要我将所有内容编写两次。听起来对我来说是肮脏的方式。

我想到的最后一个解决方案是创建一个内部目标FooInternal,将每个标头和源都设置为public,然后FooTest可以链接到该目标。然后将包装库创建为Foo,它与FooInternal链接为私有,但将include/Foo/文件夹设置为公共头。 但这需要Foo作为接口目标,然后将没有任何include/Foo导出,需要我创建自定义逻辑来重命名,或者以某种方式设置CMake以将libFoo用作正确的lib文件。同样,这听起来很脏,我不确定这在实践中如何工作。

我是否遗忘了任何明显的解决方案,或者有人对这个问题有好的解决方案?

1 个答案:

答案 0 :(得分:0)

edit3: edit2的解决方案实际上不适用于包含多个库的构建树,因为它使目标可以访问链接的目标内部标头。 布拉德·金(Brad King)在Kitwares CMake gitlab上最终使用的解决方案是

add_library(foo foo.c)
target_include_directories(foo PUBLIC "$<BUILD_INTERFACE:$<$<BOOL:$<TARGET_PROPERTY:FOO_PRIVATE>>:/path/to/foo/private/include>>")
set_property(TARGET foo PROPERTY FOO_PRIVATE 1)

add_executable(test_foo test_foo.c)
target_link_libraries(test_foo foo)
set_property(TARGET test_foo PROPERTY FOO_PRIVATE 1)

来源:https://gitlab.kitware.com/cmake/cmake/issues/19048


edit2:最终的解决方案非常简单,那就是将包含路径设置为

target_include_directories(Foo
    PUBLIC
        $<INSTALL_INTERFACE:include>
        $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
)

edit1:看完视频链接后,显示我最近进行的编程和测试太少,而面向devop的工作却过多。 我需要能够测试内部标头。回到有关如何使用给定设置使用CMake正确测试的原始问题。当我找到令人信服的解决方案时,将更新此答案。


原文:在听过Superlokkus对cpplang松弛的评论和讨论之后,似乎最好不要测试内部标头。 如果使用公共头文件和最里面的.cpp文件的测试之间的抽象数量太大,请将其拆分为独立的库。