CMake: 带有单元测试的项目结构

我试图将我的项目结构化,包括生产资源(在 src子文件夹中)和测试(在 test子文件夹中)。我正在使用 CMake 来构建这个。作为一个最小的示例,我有以下文件:

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (TEST)


add_subdirectory (src)
add_subdirectory (test)

Src/CMakeLists.txt:

add_executable (demo main.cpp sqr.cpp)

Src/sqr.h

#ifndef SQR_H
#define SQR_H
double sqr(double);
#endif // SQR_H

Src/sqr.cpp

#include "sqr.h"
double sqr(double x) { return x*x; }

Src/main.cpp-使用 sqr,这并不重要

Test/CMakeLists.txt:

find_package(Boost COMPONENTS system filesystem unit_test_framework REQUIRED)


include_directories (${TEST_SOURCE_DIR}/src)


ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK)


add_executable (test test.cpp ${TEST_SOURCE_DIR}/src/sqr.cpp)


target_link_libraries(test
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
)


enable_testing()
add_test(MyTest test)

Test/test.cpp:

#define BOOST_TEST_MODULE SqrTests
#include <boost/test/unit_test.hpp>


#include "sqr.h"


BOOST_AUTO_TEST_CASE(FailTest)
{
BOOST_CHECK_EQUAL(5, sqr(2));
}


BOOST_AUTO_TEST_CASE(PassTest)
{
BOOST_CHECK_EQUAL(4, sqr(2));
}

有几个问题:

  1. 这个结构说得通吗?构造此代码时的最佳实践是什么?(我来自 C # 和 java,从某种意义上说,它更容易)
  2. 我不喜欢这个事实,我必须列出所有的文件从 src文件夹中的 test/CMakeLists.txt文件。如果这是一个图书馆项目,我会直接链接图书馆。有没有办法避免列出来自其他项目的所有 cpp 文件?
  3. 线路 enable_testing()add_test(MyTest test)在做什么?我没看到任何效果。如何从 CMake (或 CTest)运行测试?
  4. 到目前为止,我只是在根文件夹中运行 cmake .,但这导致到处都是临时文件。如何使编译结果具有合理的结构?
125132 次浏览

对于问题1和2,我建议使用不包括 main.cpp 的非测试文件建立一个库(在这种情况下只使用 src/sqr.cpp 和 src/sqr.h) ,这样就可以避免列出(更重要的是重新编译)所有源代码两次。

对于问题3,这些命令添加一个名为“ MyTest”的测试,该测试不带任何参数地调用可执行的“ test”。但是,由于您添加了这些命令来测试/CMakeLists.txt,而不是您的顶级 CMakeLists.txt,因此您只能从构建树的“ test”子目录(尝试 cd test && ctest -N)中调用测试。如果希望测试能够在顶级构建目录中运行,则需要从顶级 CMakeLists.txt 调用 add_test。这也意味着您必须使用更详细的 add_test格式,因为您的测试 exe 不是在相同的 CMakeLists.txt 中定义的

在您的例子中,因为您在根文件夹中运行 cmake,所以您的构建树和源树是一样的。这就是众所周知的内部源代码构建,并不理想,这就导致了问题4。

生成构建树的首选方法是执行源代码外构建,即在源代码树之外的某个地方创建一个目录,并从那里执行 cmake。即使在项目的根目录中创建一个“ build”目录并执行 cmake ..,也会提供一个干净的结构,不会干扰源树。

最后一点是避免调用可执行文件“ test”(区分大小写)。

为了实现这些改变,我会做以下事情:

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (TEST)
add_subdirectory (src)
add_subdirectory (test)
enable_testing ()
add_test (NAME MyTest COMMAND Test)

Src/CMakeLists.txt:
add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)

Test/CMakeLists.txt:
find_package (Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
include_directories (${TEST_SOURCE_DIR}/src
${Boost_INCLUDE_DIRS}
)
add_definitions (-DBOOST_TEST_DYN_LINK)
add_executable (Test test.cpp)
target_link_libraries (Test
Sqr
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
)

我喜欢@Fraser 的示例,但是会在 test/CMakeLists.txt 中使用 add _ test 命令,并在 add _ subdirectory (test)之前使用 able _ test。

这样,您可以在 test/CMakeLists.txt 中指定测试时,从顶级构建目录运行测试。

结果如下所示(我重用了@Fraser 的例子) :

CMakeLists.txt

cmake_minimum_required (VERSION 2.8)
project (TEST)
add_subdirectory (src)


enable_testing ()
add_subdirectory (test)

Src/CMakeLists.txt

add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)

Test/CMakeLists.txt

find_package (Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
include_directories (${TEST_SOURCE_DIR}/src
${Boost_INCLUDE_DIRS}
)
add_definitions (-DBOOST_TEST_DYN_LINK)
add_executable (Test test.cpp)
target_link_libraries (Test
Sqr
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
)
add_test (NAME MyTest COMMAND Test)