前言 CMake 是一个跨平台的 C/C++ 项目组织管理工具,虽然许多 IDE 都有私有的项目管理工具,但是在现在各大 IDE 基本都支持使用 CMake 管理项目,所以如果有跨平台的需求,使用 CMake 管理是最方便的。值得一提的是,CMake 支持 gtest 、cppunit
等单元测试框架,当然也可以使用断言自定义单元测试。
创建简单的带单元测试的项目 创建项目工程 下载代码
点击下载 完整的案例代码,项目的目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 minder-test ├── CMakeLists.txt ├── include │ └── datetime.h ├── src │ ├── datetime.cpp │ └── main.cpp └── test ├── CMakeLists.txt ├── include │ └── strUtil.h └── src └── main.cpp
编写项目代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #pragma once #include <iostream> #include <sstream> using namespace std;class DateUtil {public : static string formatCurrentTime () ; static string formatCurrentTime (string format) ; static int dayOfWeek (const string &date) ; static bool isWeekendDays (const string &date) ; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include "datetime.h" string DateUtil::formatCurrentTime () { time_t rawtime; struct tm *info ; char buffer[80 ]; time (&rawtime); info = localtime (&rawtime); strftime (buffer, 80 , "%Y-%m-%d %H:%M:%S" , info); string str (buffer) ; return str; } string DateUtil::formatCurrentTime (string format) { time_t rawtime; struct tm *info ; char buffer[80 ]; time (&rawtime); info = localtime (&rawtime); strftime (buffer, 80 , format.c_str (), info); string str (buffer) ; return str; } int DateUtil::dayOfWeek (const string &date) { char c; int y, m, d; stringstream (date) >> y >> c >> m >> c >> d; tm t = {0 , 0 , 0 , d, m - 1 , y - 1900 }; mktime (&t); return t.tm_wday; } bool DateUtil::isWeekendDays (const string &date) { int wday = dayOfWeek (date); if (wday == 6 || wday == 0 ) { return true ; } return false ; }
1 2 3 4 5 6 7 8 9 10 #include <iostream> #include "datetime.h" using namespace std;int main () { cout << DateUtil::formatCurrentTime() << endl; cout << DateUtil::formatCurrentTime("%Y-%m-%d" ) << endl; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #pragma once #include <iostream> using namespace std;void trim (string &str) { if (str.empty ()) { return ; } str.erase (0 , str.find_first_not_of (" " )); str.erase (str.find_last_not_of (" " ) + 1 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> #include "strUtil.h" #include "datetime.h" using namespace std;int main () { string str = " Hello World ! " ; trim (str); cout << str << endl; cout << "wday = " << DateUtil::dayOfWeek ("2022-01-11" ) << ", " ; cout << "isWeekendDays = " << (DateUtil::isWeekendDays ("2022-01-11" ) ? "true" : "false" ) << endl; return 0 ; }
其中 test
目录可以视作为子项目,和主目录分开编译。为了模拟更真实的企业项目开发场景,这里的 test/src/main.cpp
同时引入了 datetime.h
和 strUtil.h
头文件。
创建配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 cmake_minimum_required (VERSION 3.15 )project (minder)set (CMAKE_CXX_STANDARD 11 )set (CMAKE_CXX_FLAGS "-g" )set (CMAKE_CXX_FLAGS "-Wall" )set (PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR} /build)include_directories (${PROJECT_SOURCE_DIR} /include )aux_source_directory (${PROJECT_SOURCE_DIR} /src MAIN_SOURCES)add_executable (${PROJECT_NAME} ${MAIN_SOURCES} )enable_testing ()add_subdirectory (test )add_test (minder_test ${PROJECT_SOURCE_DIR} /test /build/minder_test)
特别说明:
set(CMAKE_CXX_FLAGS "-xxx")
:指定编译参数,细化的还有 CMAKE_CXX_FLAGS_DEBUG
和 CMAKE_CXX_FLAGS_RELEASE
add_subdirectory(xxx)
:添加子目录(子项目),要求子目录里必须有单独的 CMakeLists.txt
,该文件包含了子目录的编译配置信息add_test(xxx ${PROJECT_SOURCE_DIR}/test/build/xxx)
:第一个参数是某个单元测试的名称,第二个参数是该单元测试的可执行文件的路径1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 cmake_minimum_required (VERSION 3.15 )project (minder_test)set (CMAKE_CXX_STANDARD 11 )include_directories (../include )aux_source_directory (../src MAIN_SOURCES)list (FILTER MAIN_SOURCES EXCLUDE REGEX "main.cpp" )include_directories (${PROJECT_SOURCE_DIR} /include )aux_source_directory (${PROJECT_SOURCE_DIR} /src TEST_SOURCES)set (PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR} /build)add_executable (${PROJECT_NAME} ${MAIN_SOURCES} ${TEST_SOURCES} )
这里的 test
作为子项目,主要要生成单元测试的可执行文件。
命令行编译项目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ cd minder-test /test $ mkdir build$ cd build$ cmake ..$ make $ ./minder_test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ cd minder-test $ mkdir build$ cd build$ cmake ..$ make $ make test $ ./minder
CMake 使用 GoogleTest 测试框架 GoogleTest 的安装 GoogleTest 编译安装 注意事项
GoogleTest 最新版(1.11.0
)要求使用 GCC 5.0+
和 Clang 5.0+
,若 GCC 的版本比较低,建议安装 GoogleTest 1.10.0
或者 1.8.1
版本 实测 GCC 4.8.5
可以正常使用 GoogleTest 的 1.10.0
版本,不兼容 1.11.0
版本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz$ tar -xvf release-1.11.0.tar.gz$ cd googletest-release-1.11.0$ mkdir build$ cd build$ cmake -DBUILD_SHARED_LIBS =ON -Dgtest_build_samples =ON ..$ make -j4 $ make install
值得一提的是,安装命令执行完成后,会自动将 libgmock_main.so
、libgmock.so
、libgtest_main.so
、libgtest.so
库文件拷贝到 /usr/local/lib64/
目录下。GoogleTest 的头文件则会安装在 /usr/local/include/gmock
和 /usr/local/include/gtest/
目录。
GoogleTest 验证安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> #include <stdlib.h> #include <gtest/gtest.h> TEST ( COutputPopLimitStrategyTest, PositiveNos ){ EXPECT_EQ (true , true ); } int main ( int argc, char *argv[] ) { ::testing::InitGoogleTest ( &argc, argv ); return (RUN_ALL_TESTS () ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ g ++ -std =c++11 test .cpp -lpthread /usr/local /lib64/libgtest.so -o test $ ./test [==========] Running 1 test from 1 test suite. [----------] Global test environment set -up. [----------] 1 test from COutputPopLimitStrategyTest [ RUN ] COutputPopLimitStrategyTest.PositiveNos [ OK ] COutputPopLimitStrategyTest.PositiveNos (0 ms) [----------] 1 test from COutputPopLimitStrategyTest (1 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test suite ran. (1 ms total) [ PASSED ] 1 test .
G++ 编译参数说明:
-std=c++11
:指定 C++ 的版本/usr/local/lib64/libgtest.so
:链接 GoogleTest 的动态链接库-lpthread
:由于 GoogleTest 的内部使用了多线程,因此需要链接 pthread
库Google Test 的使用案例 创建项目工程 下载代码
点击下载 完整的案例代码,项目的目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 minder-gtest ├── CMakeLists.txt ├── include │ └── datetime.h ├── src │ ├── datetime.cpp │ └── main.cpp └── test ├── CMakeLists.txt ├── include │ └── strUtil.h └── src └── main.cpp
编写项目代码 下载代码
这里的 C++ 代码,除了 main.cpp
的代码不一样之外,其他代码与上面的案例代码完全一致,不再累述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> #include "strUtil.h" #include "datetime.h" #include <gtest/gtest.h> using namespace std;TEST (TestCase, test1) { string str = " Hello World ! " ; trim (str); ASSERT_EQ ("Hello World !" , str); } TEST (TestCase, test2) { ASSERT_EQ (true , DateUtil::isWeekendDays ("2022-01-09" )); } int main (int argc, char **argv) { testing::InitGoogleTest (&argc, argv); return RUN_ALL_TESTS (); }
CMake 配置文件 主目录的 CMakeLists.txt
,这里的配置内容与上面的案例没有任何区别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 cmake_minimum_required (VERSION 3.15 )project (minder)set (CMAKE_CXX_STANDARD 11 )set (CMAKE_CXX_FLAGS "-g" )set (CMAKE_CXX_FLAGS "-Wall" )set (PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR} /build)include_directories (${PROJECT_SOURCE_DIR} /include )aux_source_directory (${PROJECT_SOURCE_DIR} /src MAIN_SOURCES)add_executable (${PROJECT_NAME} ${MAIN_SOURCES} )enable_testing ()add_subdirectory (test )add_test (minder_test ${PROJECT_SOURCE_DIR} /test /build/minder_test)
test
目录的 CMakeLists.txt
,这里的配置内容新增了 GoogleTest 库1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 cmake_minimum_required (VERSION 3.15 )project (minder_test)set (CMAKE_CXX_STANDARD 11 )find_package (GTest REQUIRED)MESSAGE (STATUS "GTEST_INCLUDE_DIRS : " ${GTEST_INCLUDE_DIRS} )MESSAGE (STATUS "GTEST_BOTH_LIBRARIES : " ${GTEST_BOTH_LIBRARIES} )include_directories (../include )aux_source_directory (../src MAIN_SOURCES)list (FILTER MAIN_SOURCES EXCLUDE REGEX "main.cpp" )include_directories (${PROJECT_SOURCE_DIR} /include )aux_source_directory (${PROJECT_SOURCE_DIR} /src TEST_SOURCES)include_directories (${GTEST_INCLUDE_DIRS} )set (PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR} /build)add_executable (${PROJECT_NAME} ${MAIN_SOURCES} ${TEST_SOURCES} )target_link_libraries (${PROJECT_NAME} ${GTEST_BOTH_LIBRARIES} pthread)
命令行编译项目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ cd minder-gtest/test $ mkdir build$ cd build$ cmake ..$ make $ ./minder_test
运行可执行文件后,输出的日志信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 [==========] Running 2 tests from 1 test suite. [----------] Global test environment set-up. [----------] 2 tests from TestCase [ RUN ] TestCase.test1 [ OK ] TestCase.test1 (0 ms) [ RUN ] TestCase.test2 [ OK ] TestCase.test2 (0 ms) [----------] 2 tests from TestCase (0 ms total) [----------] Global test environment tear-down [==========] 2 tests from 1 test suite ran. (2 ms total) [ PASSED ] 2 tests.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ cd minder-gtest$ mkdir build$ cd build$ cmake ..$ make $ make test $ ./minder
GoogleTest 使用扩展说明 在上面的案例中,GoogleTest 是使用源码编译的方式安装到 Linux 系统上的,这在迁移操作系统的时候,需要重复执行同样的安装步骤。此时为了方便日后迁移操作系统,可以将 GoogleTest 的头文件、动态链接都复制一份到项目中,这样就可以不依赖外部的系统环境了。
提示
点击下载 完整的案例代码,项目的目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 minder-gtest-plus ├── CMakeLists.txt ├── include │ └── datetime.h ├── src │ ├── datetime.cpp │ └── main.cpp ├── test │ ├── CMakeLists.txt │ ├── include │ │ └── strUtil.h │ └── src │ └── main.cpp └── thirdparty └── googletest ├── gmock │ ├── include │ │ └── gmock │ │ ├── gmock-actions.h │ │ ├── gmock-cardinalities.h │ │ ├── gmock-function-mocker.h │ │ ├── gmock-generated-actions.h │ │ ├── gmock-generated-actions.h.pump │ │ ├── gmock-generated-function-mockers.h │ │ ├── gmock-generated-function-mockers.h.pump │ │ ├── gmock-generated-matchers.h │ │ ├── gmock-generated-matchers.h.pump │ │ ├── gmock.h │ │ ├── gmock-matchers.h │ │ ├── gmock-more-actions.h │ │ ├── gmock-more-matchers.h │ │ ├── gmock-nice-strict.h │ │ ├── gmock-spec-builders.h │ │ └── internal │ │ ├── custom │ │ │ ├── gmock-generated-actions.h │ │ │ ├── gmock-generated-actions.h.pump │ │ │ ├── gmock-matchers.h │ │ │ ├── gmock-port.h │ │ │ └── README.md │ │ ├── gmock-internal-utils.h │ │ ├── gmock-port.h │ │ └── gmock-pp.h │ └── lib │ ├── libgmock_main.so │ └── libgmock.so └── gtest ├── include │ └── gtest │ ├── gtest-death-test.h │ ├── gtest.h │ ├── gtest-matchers.h │ ├── gtest-message.h │ ├── gtest-param-test.h │ ├── gtest_pred_impl.h │ ├── gtest-printers.h │ ├── gtest_prod.h │ ├── gtest-spi.h │ ├── gtest-test-part.h │ ├── gtest-typed-test.h │ └── internal │ ├── custom │ │ ├── gtest.h │ │ ├── gtest-port.h │ │ ├── gtest-printers.h │ │ └── README.md │ ├── gtest-death-test-internal.h │ ├── gtest-filepath.h │ ├── gtest-internal.h │ ├── gtest-param-util.h │ ├── gtest-port-arch.h │ ├── gtest-port.h │ ├── gtest-string.h │ ├── gtest-type-util.h │ └── gtest-type-util.h.pump └── lib ├── libgtest_main.so └── libgtest.so
test
目录的 CMakeLists.txt
,这里的配置内容使用了项目里的 GoogleTest 库1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 cmake_minimum_required (VERSION 3.15 )set (PATH_TO_GOOGLE_TEST ../thirdparty/googletest/gtest)set (PATH_TO_GOOGLE_MOCK ../thirdparty/googletest/gmock)project (minder_test)set (CMAKE_CXX_STANDARD 11 )include_directories (../include )aux_source_directory (../src MAIN_SOURCES)list (FILTER MAIN_SOURCES EXCLUDE REGEX "main.cpp" )include_directories (${PROJECT_SOURCE_DIR} /include )aux_source_directory (${PROJECT_SOURCE_DIR} /src TEST_SOURCES)include_directories (${PATH_TO_GOOGLE_TEST} /include ${PATH_TO_GOOGLE_MOCK} /include )link_directories (${PATH_TO_GOOGLE_TEST} /lib ${PATH_TO_GOOGLE_MOCK} /lib)set (PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR} /build)add_executable (${PROJECT_NAME} ${MAIN_SOURCES} ${TEST_SOURCES} )target_link_libraries (${PROJECT_NAME} gtest_main.so gtest.so gmock_main.so gmock.so pthread)
main.cpp
的 C++ 代码,与上面的案例代码完全一致1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> #include "strUtil.h" #include "datetime.h" #include <gtest/gtest.h> using namespace std;TEST (TestCase, test1) { string str = " Hello World ! " ; trim (str); ASSERT_EQ ("Hello World !" , str); } TEST (TestCase, test2) { ASSERT_EQ (true , DateUtil::isWeekendDays ("2022-01-09" )); } int main (int argc, char **argv) { testing::InitGoogleTest (&argc, argv); return RUN_ALL_TESTS (); }
参考博客