1. 项目概述与背景最近在重构一个老旧的C服务端项目为了提升代码质量和开发效率决定引入单元测试。在众多C测试框架中Google Test简称gtest以其稳定、功能全面和与CMake的良好集成而闻名成为了我的首选。我的开发环境是一台运行CentOS 7的服务器虽然这个系统版本已经有些年头但在很多企业生产环境中依然广泛使用因此在CentOS 7上部署一套稳定可用的gtest环境对于很多和我一样维护着存量C项目的开发者来说是一个很实际的需求。这个过程看似简单就是下载、编译、安装但其中涉及到依赖管理、编译选项、库文件路径配置等一系列细节任何一个环节没处理好都可能让你在后续的集成和使用中踩坑。本文将详细记录我从零开始在CentOS 7上安装Google Test并编写、运行第一个测试用例的全过程同时会分享我遇到的一些典型问题及其解决方案希望能为你提供一份可直接复现的参考。2. 环境准备与依赖安装在开始编译gtest之前确保你的CentOS 7系统处于一个干净、可编译的状态至关重要。很多编译失败的问题根源都在于缺失必要的开发工具和库。2.1 系统更新与基础开发工具首先我们需要更新系统并安装最基础的编译工具链。使用yum包管理器执行以下命令。这里我强烈建议先配置一个国内的yum镜像源如阿里云、腾讯云可以极大提升软件包下载速度。# 1. 更新系统现有软件包非必须但推荐 sudo yum update -y # 2. 安装开发工具组这包含了gcc, g, make, autoconf等核心工具 sudo yum groupinstall -y Development Tools # 3. 安装CMake。gtest官方推荐使用CMake进行构建这是现代C项目的事实标准。 # CentOS 7默认仓库的CMake版本可能较低我们可以通过EPEL仓库安装较新版本。 sudo yum install -y epel-release sudo yum install -y cmake3 # 创建软链接让系统默认的cmake命令指向cmake3 sudo ln -sf /usr/bin/cmake3 /usr/bin/cmake # 验证安装 cmake --version g --version注意如果你计划将gtest集成到使用autotools如./configure make的老项目中那么autoconf、automake、libtool这些工具也是必需的它们已经包含在Development Tools组里了。专注于CMake是现代项目更简洁的选择。2.2 获取Google Test源代码我们不推荐直接通过yum安装可能存在的陈旧gtest开发包因为版本不可控且可能缺少某些特性。从GitHub获取最新稳定版源码自行编译是最佳实践。# 1. 安装git如果尚未安装 sudo yum install -y git # 2. 选择一个合适的目录克隆Google Test仓库 # 这里我们克隆并切换到最新的稳定分支例如v1.14.0请以GitHub发布页为准 cd /usr/local/src # 或者任何你习惯的源码存放目录 sudo git clone https://github.com/google/googletest.git cd googletest # 查看并切换到最新的稳定标签避免使用可能不稳定的main分支 sudo git checkout release-1.14.0使用release分支而非main分支是为了保证代码的稳定性和可复现性。生产环境应避免使用处于活跃开发状态的主干代码。3. 编译与安装决策拿到源码后我们面临一个关键决策如何编译和“安装”gtest这里有两种主流模式理解它们的区别直接影响你后续项目的使用方式。3.1 模式一作为项目内嵌源码推荐用于项目级使用这种模式不进行系统级的make install而是将gtest的源码作为你项目的一部分通常放在third_party或extern目录下在你的项目构建时如通过CMake的add_subdirectory直接编译。这是Google官方推荐的方式尤其是在CI/CD环境中因为它能保证测试框架的版本与你的项目完全绑定避免了因系统全局gtest版本不同而导致的兼容性问题。优点版本隔离每个项目可以使用特定版本的gtest互不影响。可移植性强项目包含了所有依赖在任何有编译环境的机器上都能一致地构建。CI/CD友好无需在构建代理机上预装gtest。缺点每个项目都需要编译一次gtest略微增加构建时间。源码会占用项目仓库的一些空间。3.2 模式二系统级安装适用于多项目共享或学习这种模式将gtest编译成静态库或动态库并安装到系统目录如/usr/local这样多个项目都可以像使用标准库一样链接它。这更像我们安装常规软件库的方式。优点一次编译多处使用方便在个人开发机上快速搭建测试环境。干净简洁项目CMakeLists.txt配置简单。缺点版本冲突如果不同项目需要不同版本的gtest会产生冲突。依赖系统环境部署到新机器时需要确保该机器也已安装相同版本的gtest。考虑到本文的目的是提供一个通用、可记录的安装使用指南并且许多初学者更习惯于“安装到系统”的模式我们将采用模式二进行演示。但我会在关键步骤指出如果采用模式一你的项目CMakeLists.txt应该如何调整。4. 使用CMake进行编译与安装我们使用CMake来构建这是最规范且跨平台的方式。# 确保你在googletest源码根目录 cd /usr/local/src/googletest # 1. 创建一个独立的构建目录保持源码树干净 sudo mkdir build cd build # 2. 运行CMake生成构建文件。 # -DCMAKE_INSTALL_PREFIX/usr/local 指定安装前缀头文件和库将安装到/usr/local/include和/usr/local/lib # -DBUILD_SHARED_LIBSON 编译为动态库(.so)默认为静态库(.a)。动态库节省磁盘空间静态库部署简单。按需选择。 sudo cmake .. -DCMAKE_INSTALL_PREFIX/usr/local -DBUILD_SHARED_LIBSON # 3. 编译。-j$(nproc) 参数表示使用所有CPU核心并行编译加快速度。 sudo make -j$(nproc) # 4. 安装到系统。这会将编译好的库和头文件拷贝到CMAKE_INSTALL_PREFIX指定的目录下。 sudo make install关键参数解析-DCMAKE_INSTALL_PREFIX/usr/local这是类Unix系统的传统本地软件安装位置。你也可以设置为/opt/gtest等自定义路径但需要相应地调整项目的链接路径。-DBUILD_SHARED_LIBSON我选择生成动态库。如果你希望生成静态库则省略此参数或设置为OFF。静态库在链接后会嵌入到你的可执行文件中使得发布更简单无需附带.so文件但会增加最终程序的大小。安装后的结果头文件被安装到/usr/local/include/gtest/和/usr/local/include/gmock/。库文件被安装到/usr/local/lib64/或/usr/local/lib/取决于系统架构。你会看到类似libgtest.solibgtest_main.so的文件动态库或.a文件静态库。CMake配置文件一个非常重要的文件GTestConfig.cmake会被安装到/usr/local/lib64/cmake/GTest/下。这允许其他CMake项目使用find_package(GTest REQUIRED)来自动定位你安装的gtest。实操心得编译过程如果报错找不到-lpthread链接线程库通常是因为CMake没有自动添加线程库依赖。一个可靠的解决方法是在运行cmake命令时显式指定sudo cmake .. -DCMAKE_INSTALL_PREFIX/usr/local -DBUILD_SHARED_LIBSON -Dgtest_disable_pthreadsOFF。确保pthread库已安装(sudo yum install glibc-headers)。5. 验证安装与编写第一个测试安装完成后我们需要验证gtest是否能正常工作。最好的验证方式就是写一个简单的测试程序。5.1 创建测试项目目录结构我们创建一个独立于gtest源码的示例项目目录。mkdir ~/my_gtest_demo cd ~/my_gtest_demo5.2 编写被测函数与测试代码1. 编写一个简单的被测函数 (math_functions.h和math_functions.cpp)math_functions.h:#ifndef MATH_FUNCTIONS_H #define MATH_FUNCTIONS_H int add(int a, int b); int multiply(int a, int b); #endif // MATH_FUNCTIONS_Hmath_functions.cpp:#include math_functions.h int add(int a, int b) { return a b; } int multiply(int a, int b) { return a * b; }2. 编写Google Test测试用例 (test_math_functions.cpp)#include gtest/gtest.h #include math_functions.h // 测试 add 函数 TEST(MathFunctionsTest, AddPositiveNumbers) { EXPECT_EQ(add(2, 3), 5); EXPECT_EQ(add(0, 0), 0); } TEST(MathFunctionsTest, AddNegativeNumbers) { EXPECT_EQ(add(-1, -1), -2); EXPECT_EQ(add(-5, 10), 5); } // 测试 multiply 函数 TEST(MathFunctionsTest, MultiplyNumbers) { EXPECT_EQ(multiply(3, 4), 12); EXPECT_EQ(multiply(0, 100), 0); EXPECT_EQ(multiply(-2, 6), -12); } // 如果需要可以在这里添加 main 函数。 // 但更推荐链接 gtest_main 库它提供了默认的 main 函数。 // int main(int argc, char **argv) { // ::testing::InitGoogleTest(argc, argv); // return RUN_ALL_TESTS(); // }5.3 编写CMakeLists.txt构建项目这是连接你的代码和已安装的gtest库的关键。我们使用CMake的find_package命令。CMakeLists.txt:cmake_minimum_required(VERSION 3.10) project(MyGTestDemo VERSION 1.0.0 LANGUAGES CXX) # 设置C标准 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 寻找安装在系统中的GTest包 find_package(GTest REQUIRED) # 寻找线程库gtest依赖它 find_package(Threads REQUIRED) # 添加包含目录 include_directories(${CMAKE_CURRENT_SOURCE_DIR}) # 添加你的主库被测代码 add_library(math_func STATIC math_functions.cpp) # 添加可执行测试文件并链接库 add_executable(run_tests test_math_functions.cpp) # 链接你的主库、GTest::GTest、GTest::Main 以及线程库 target_link_libraries(run_tests math_func GTest::gtest GTest::gtest_main Threads::Threads ) # 启用测试功能并将可执行文件添加为测试用例 enable_testing() add_test(NAME MyMathTests COMMAND run_tests)5.4 构建并运行测试# 在项目根目录 (~/my_gtest_demo) 下 mkdir build cd build cmake .. make -j$(nproc) # 运行测试可执行文件 ./run_tests # 或者使用ctest命令它会运行所有通过add_test添加的测试 ctest --verbose如果一切顺利你将看到类似以下的输出[] Running 3 tests from 1 test suite. [----------] Global test environment set-up. [----------] 3 tests from MathFunctionsTest [ RUN ] MathFunctionsTest.AddPositiveNumbers [ OK ] MathFunctionsTest.AddPositiveNumbers (0 ms) [ RUN ] MathFunctionsTest.AddNegativeNumbers [ OK ] MathFunctionsTest.AddNegativeNumbers (0 ms) [ RUN ] MathFunctionsTest.MultiplyNumbers [ OK ] MathFunctionsTest.MultiplyNumbers (0 ms) [----------] 3 tests from MathFunctionsTest (0 ms total) [----------] Global test environment tear-down. [] 3 tests from 1 test suite ran. (0 ms total) [ PASSED ] 3 tests.这标志着你的Google Test已经在CentOS 7上成功安装并可以正常使用了6. 高级配置与集成技巧基本的安装运行只是第一步要让gtest更好地融入你的工作流还需要了解一些进阶配置。6.1 使用gtest_main与自定义main函数在上面的例子中我们链接了GTest::gtest_main。这个库提供了一个默认的main()函数它负责初始化gtest并运行所有测试。这对于大多数简单测试项目来说足够了。然而有时你可能需要在测试运行前后执行一些自定义初始化或清理工作例如设置全局测试环境、解析自定义命令行参数等。这时你需要编写自己的main函数并且只链接GTest::gtest不链接gtest_main。// custom_main_test.cpp #include gtest/gtest.h #include math_functions.h TEST(MyTest, Example) { EXPECT_EQ(1, 1); } int main(int argc, char **argv) { // 初始化Google Test这会将命令行参数传递给gtest ::testing::InitGoogleTest(argc, argv); // --- 在这里添加你的自定义初始化代码 --- std::cout Initializing custom test environment... std::endl; // 运行所有测试 int test_result RUN_ALL_TESTS(); // --- 在这里添加你的自定义清理代码 --- std::cout Tests finished. Cleaning up... std::endl; return test_result; }对应的CMakeLists.txt中target_link_libraries里就不要包含GTest::gtest_main了。6.2 常用gtest命令行参数通过./run_tests --help可以查看所有可用参数。几个常用的有--gtest_list_tests列出所有测试用例和测试项的名称而不运行它们。--gtest_filter*TestPattern*过滤器只运行匹配名称的测试。例如--gtest_filterMathFunctionsTest.*运行该测试套件的所有案例--gtest_filter*Add*运行所有名称包含Add的测试。--gtest_repeat1000重复运行测试多次用于稳定性测试或性能粗略评估。--gtest_break_on_failure在调试器下运行时测试失败时会触发断点。--gtest_outputxml:report.xml将测试结果输出为JUnit格式的XML报告便于CI系统如Jenkins集成展示。6.3 与CMake的CTest深度集成我们之前已经用了enable_testing()和add_test()。CTest是CMake的测试驱动程序它可以并行运行测试 (-j N)。超时控制。按标签、名称过滤测试。与CDash等仪表板集成。你可以创建一个CTestConfig.cmake文件来配置这些行为使得make test或ctest命令更加强大。7. 常见问题排查与解决方案实录在实际操作中你几乎一定会遇到一些问题。以下是我在多次部署中总结的“坑”和填坑方法。7.1 编译gtest源码时的问题问题1CMake报错提示找不到合适的C编译器。CMake Error at CMakeLists.txt:53 (project): No CMAKE_CXX_COMPILER could be found.解决你没有安装g。确保执行了sudo yum groupinstall -y Development Tools。问题2make编译时失败错误信息涉及-lpthread。/usr/bin/ld: cannot find -lpthread collect2: error: ld returned 1 exit status解决这通常是因为pthread库的链接问题。尝试以下步骤确保已安装glibc-headers和kernel-headerssudo yum install glibc-headers kernel-headers。在运行cmake时显式关闭一个可能导致问题的内部选项-Dgtest_disable_pthreadsOFF尽管名字是disable但OFF表示启用。所以完整的cmake命令可能是sudo cmake .. -DCMAKE_INSTALL_PREFIX/usr/local -DBUILD_SHARED_LIBSON -Dgtest_disable_pthreadsOFF7.2 链接自己项目时的问题问题3编译自己的测试项目时报错fatal error: gtest/gtest.h: No such file or directory。解决这表示编译器找不到gtest的头文件。如果你系统安装了确保find_package(GTest REQUIRED)成功并且使用了target_include_directories或include_directories。find_package成功后会定义GTEST_INCLUDE_DIRS这样的变量但现代用法是直接链接GTest::gtest目标它会自动传递包含目录。检查安装路径确认头文件确实在/usr/local/include/gtest下。如果没有可能是安装失败了回头检查sudo make install的输出是否有错误。问题4链接错误报错undefined reference totesting::internal::...。解决这是典型的链接器错误说明找到了头文件但没找到库文件。检查库路径运行sudo find /usr/local -name libgtest*.so -o -name libgtest*.a确认库文件存在。更新链接器缓存执行sudo ldconfig更新系统的动态链接库缓存。这对于安装在/usr/local/lib或/usr/local/lib64的非标准路径库文件是必要的。检查CMake链接语句确保target_link_libraries中正确包含了GTest::gtest和GTest::gtest_main或你自己编译的gtest库目标。如果使用静态库且你的项目也是静态链接可能需要手动添加-pthread链接选项。问题5运行测试时报错error while loading shared libraries: libgtest.so.1.14: cannot open shared object file。解决系统运行时找不到动态库.so文件。永久方案将库路径添加到系统配置。创建文件/etc/ld.so.conf.d/gtest.conf内容为/usr/local/lib64请根据你的实际库路径填写然后运行sudo ldconfig。临时方案在运行程序前设置环境变量export LD_LIBRARY_PATH/usr/local/lib64:$LD_LIBRARY_PATH。7.3 关于版本与兼容性问题6find_package(GTest REQUIRED)失败提示找不到GTestConfig.cmake。解决这通常发生在你通过make install安装后。find_package的CONFIG模式会去特定路径如/usr/local/lib64/cmake/查找GTestConfig.cmake文件。请确保你确实运行了sudo make install。使用-DCMAKE_INSTALL_PREFIX/usr/local安装的。检查该目录下是否存在GTest文件夹及其.cmake文件。如果不存在可能是CMake版本或gtest版本的问题。可以尝试回退到gtest的旧版本如1.10.0或者考虑使用源码内嵌模式模式一通过add_subdirectory(googletest)来引入这样就完全不需要find_package了。我个人在CentOS 7上更倾向于使用源码内嵌模式尤其是在为特定项目配置CI时。这样做的确定性最高避免了所有因系统环境差异导致的问题。具体做法是将googletest作为git submodule添加到你的项目中然后在主CMakeLists.txt里add_subdirectory(third_party/googletest) # ... target_link_libraries(your_test_target your_library gtest gtest_main pthread )这种方式下你甚至不需要在系统层面“安装”gtest所有依赖都在项目内部解决了。