Ubuntu安装Google Test:C++单元测试环境搭建与CMake集成指南
2026/7/30 7:46:24 网站建设 项目流程

1. 项目概述:为什么要在Ubuntu上安装Google Test?

如果你在Linux环境下用C++做开发,尤其是涉及到单元测试,那么Google Test(简称gtest)几乎是一个绕不开的框架。它由Google开源,设计优雅,功能强大,是C++社区进行单元测试的事实标准之一。很多开源项目,比如LevelDB、Protobuf,其测试套件都构建在gtest之上。在Ubuntu上安装gtest,意味着你为自己搭建了一个符合工业级标准的C++测试环境,无论是验证自己写的算法库,还是为团队项目构建持续集成(CI)流程,这都是基础且关键的一步。

我最初接触gtest是在一个需要保证高可靠性的后台服务项目中。代码逻辑复杂,手动测试效率低下且容易遗漏。引入gtest后,我们不仅能够为每个核心函数编写独立的测试用例,还能通过TEST_FSetUp/TearDown轻松模拟各种复杂的初始化和清理场景。更重要的是,gtest生成的测试报告非常清晰,能快速定位失败的断言和具体行号,极大提升了调试效率。在Ubuntu这个主流的开发和生产环境中,掌握gtest的安装和使用,是提升C++工程化能力的必备技能。

2. 安装前的环境准备与方案选型

在动手安装之前,我们需要明确目标和环境。你手头应该有一台运行Ubuntu的机器,无论是物理机、虚拟机还是WSL2子系统。我这里以Ubuntu 22.04 LTS为例,其他版本如20.04、24.04的操作大同小异。首先,打开你的终端。

2.1 系统更新与基础编译环境

安装任何从源码构建的软件,一个健全的编译环境是前提。第一步永远是更新软件源并安装必要的工具链。

sudo apt update sudo apt upgrade -y

这条命令会更新本地软件包列表并升级所有可升级的包。-y参数用于自动确认,避免中途等待。接下来,安装编译gtest所需的工具:

sudo apt install -y build-essential cmake git pkg-config

我们来拆解一下这几个包:

  • build-essential: 这是Ubuntu下的“开发套件”元包,包含了gccg++makelibc-dev等最基础的编译工具。没有它,你连最简单的hello world都编译不了。
  • cmake: gtest官方推荐使用CMake来构建。CMake是一个跨平台的自动化构建系统生成器,它能生成Makefile,然后由make命令执行编译。现代C++项目大多采用CMake,提前熟悉它没坏处。
  • git: 用于从GitHub克隆gtest的源代码仓库。这是获取最新代码最直接的方式。
  • pkg-config: 一个帮助你在编译和链接时查找库文件(.so)和头文件(.h)的工具。虽然gtest安装后我们可能不直接用它,但作为一个完整的开发环境,装上它以备不时之需。

注意:如果你的Ubuntu是最小化安装,可能连sudo都没有。这时你需要先以root身份执行apt install sudo,然后将你的用户添加到sudo组。不过对于大多数桌面版或标准服务器版Ubuntu,这一步可以跳过。

2.2 安装方案对比:系统包、源码编译与Conan

安装gtest主要有三种途径,各有优劣,选择哪种取决于你的具体需求。

方案一:使用APT包管理器安装(最快捷)

sudo apt install -y libgtest-dev

这是Ubuntu官方仓库提供的包。它的优点是极其简单,一条命令搞定。但缺点也很明显:

  1. 版本通常较旧:Ubuntu为了稳定性,仓库中的软件版本更新较慢。例如Ubuntu 22.04提供的可能是1.10.x版本,而GitHub主线可能已经到了1.14.x。你可能会错过一些新特性和Bug修复。
  2. 只包含头文件和源码libgtest-dev这个包其实只安装了头文件(在/usr/include/gtest)和源码(在/usr/src/gtest)。它没有预编译好的库文件(.a.so)。你需要手动进入/usr/src/gtest目录,用CMake编译出库文件才能使用。对于新手,这反而增加了步骤。

方案二:从GitHub源码编译安装(最推荐、最灵活)这是我最常用也是最为推荐的方式。直接从Google的GitHub仓库拉取最新代码进行编译安装。

  • 优点
    • 获取最新版本,享受最新特性和修复。
    • 编译参数完全可控(如编译为静态库还是动态库,是否开启特定功能)。
    • 安装路径可以自定义,方便管理。
    • 过程透明,有助于理解库的构建过程。
  • 缺点:步骤稍多,需要手动操作。

方案三:使用Conan或vcpkg等C++包管理器(面向现代项目)如果你的项目已经采用了Conan或vcpkg来管理第三方依赖,那么通过它们来安装gtest是最优雅的。

  • 优点:依赖管理自动化,版本锁定精准,跨平台一致性极好。
  • 缺点:需要额外学习包管理器的使用,对于小型或一次性项目有点“杀鸡用牛刀”。

对于绝大多数希望学习、控制细节的开发者,方案二(源码编译)是最佳选择。它不仅教你如何安装,更让你理解一个C++库是如何从源码变成可用的二进制文件的。接下来,我们就详细走通这条路。

3. 从源码编译安装Google Test全流程

3.1 获取最新源代码

首先,我们找一个合适的位置来存放源码。通常我会在用户主目录下创建一个srcrepos文件夹来存放各种项目的源代码。

cd ~ mkdir -p src cd src

使用git克隆官方仓库。这里注意,Google Test和Google Mock现在已经合并到同一个仓库中,名为googletest

git clone https://github.com/google/googletest.git cd googletest

克隆完成后,你可以通过git tag查看所有发布版本标签,如果你想使用某个特定稳定版而非最新的开发主线,可以使用git checkout v1.14.0这样的命令切换到指定标签。为了演示,我们直接使用主分支的最新代码。

3.2 使用CMake配置与编译

现在进入仓库根目录,创建一个独立的构建目录。这是一个非常好的实践,被称为“Out-of-source build”,它能保证源码目录的纯净,所有编译产生的中间文件都放在另一个目录里。

mkdir build cd build

接下来,使用CMake生成构建系统文件。这里有几个关键参数需要理解:

cmake .. -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/local
  • ..: 表示CMakeLists.txt文件在上一级目录(即googletest/)。
  • -DCMAKE_CXX_STANDARD=17: 指定编译gtest库本身时使用的C++标准。这里设为C++17,确保生成的库能兼容使用C++17及以下标准的项目。如果你的项目用C++11或14,这里也可以相应调整。
  • -DBUILD_SHARED_LIBS=ON: 这个选项至关重要。它告诉CMake将gtest编译成动态链接库(.so文件)。如果设为OFF,则编译为静态库(.a文件)。
    • 动态库 vs 静态库:动态库在程序运行时才被加载,多个程序可以共享内存中的同一份库代码,节省磁盘和内存空间,更新库时无需重新编译所有程序。静态库则会被直接链接到你的可执行文件中,使得程序体积变大,但部署更简单,因为不依赖外部库文件。对于像gtest这样的基础库,我个人更倾向于使用动态库,因为它更符合Linux包管理的哲学,也便于多个测试程序共享。
  • -DCMAKE_INSTALL_PREFIX=/usr/local: 指定安装的根目录。/usr/local是Linux系统存放用户本地安装软件的标准位置。库文件会安装到/usr/local/lib,头文件会安装到/usr/local/include。系统会自动在这些路径下查找库和头文件。

执行完cmake命令后,终端会输出一系列检查信息,如编译器版本、找到的包等。如果没有报错,就可以开始编译了:

make -j$(nproc)
  • make: 执行编译。
  • -j$(nproc): 这是一个非常实用的技巧。nproc命令会返回你CPU的核心数,$(nproc)将其作为参数传递给-j-j选项用于指定并行编译的作业数。例如,如果你的CPU是8核,这条命令就相当于make -j8,会启动8个任务同时编译,能极大缩短编译时间,充分利用多核性能。

编译过程可能需要一两分钟,取决于你的机器性能。完成后,你可以在build/lib目录下看到生成的库文件,通常是libgtest.solibgtest_main.solibgmock.solibgmock_main.so等。

3.3 安装到系统目录

编译成功后,将库文件和头文件安装到之前指定的/usr/local目录:

sudo make install

sudo是必需的,因为向/usr/local写入文件需要管理员权限。这条命令会:

  1. 将编译好的.so动态库文件复制到/usr/local/lib
  2. 将所有的头文件(.h)复制到/usr/local/include下的gtestgmock目录。

安装完成后,系统级的动态链接器需要更新一下缓存,以便它能立刻找到新安装的库:

sudo ldconfig

3.4 验证安装是否成功

如何确认gtest已经正确安装了呢?我们来写一个最简单的测试程序验证一下。

在你喜欢的位置(比如~/test_gtest)创建一个测试文件hello_test.cpp

// hello_test.cpp #include <gtest/gtest.h> // 一个简单的函数,用于测试 int Add(int a, int b) { return a + b; } // 定义一个测试用例 TEST(TestAdd, PositiveNumbers) { EXPECT_EQ(Add(1, 2), 3); EXPECT_EQ(Add(10, 20), 30); } TEST(TestAdd, NegativeNumbers) { EXPECT_EQ(Add(-1, -2), -3); EXPECT_EQ(Add(-10, 20), 10); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }

然后,使用g++编译这个测试程序。关键是要告诉编译器头文件在哪里找,以及链接哪个库。

g++ -std=c++17 -o hello_test hello_test.cpp -lgtest -lgtest_main -pthread
  • -std=c++17: 指定使用C++17标准编译你的测试代码。
  • -o hello_test: 指定输出的可执行文件名为hello_test
  • hello_test.cpp: 你的源代码文件。
  • -lgtest: 链接libgtest.so动态库。编译器会在默认库路径(如/usr/local/lib/usr/lib)中查找名为libgtest.so的文件。
  • -lgtest_main: 链接libgtest_main.so库。这个库提供了一个默认的main()函数。如果你像上面代码一样自己写了main函数,就不需要链接这个库。如果省略自定义的main,链接gtest_main可以让gtest自动提供入口函数,简化代码。
  • -pthread:这是非常关键且容易遗漏的一步!gtest内部使用了多线程,因此必须链接POSIX线程库。忘记这个参数会导致链接错误,提示undefined reference to ‘pthread_’之类的错误。

最后,运行编译生成的可执行文件:

./hello_test

如果看到类似下面的输出,恭喜你,安装成功了!

[==========] Running 2 tests from 1 test suite. [----------] Global test environment set-up. [----------] 2 tests from TestAdd [ RUN ] TestAdd.PositiveNumbers [ OK ] TestAdd.PositiveNumbers (0 ms) [ RUN ] TestAdd.NegativeNumbers [ OK ] TestAdd.NegativeNumbers (0 ms) [----------] 2 tests from TestAdd (0 ms total) [----------] Global test environment tear-down. [==========] 2 tests from 1 test suite ran. (0 ms total) [ PASSED ] 2 tests.

4. 集成到CMake项目的最佳实践

在实际项目中,我们很少直接用g++命令行编译,而是使用CMake来管理构建。将gtest集成到你的CMake项目中,才是更工程化的做法。

假设你的项目结构如下:

my_project/ ├── CMakeLists.txt ├── include/ │ └── my_math.h ├── src/ │ └── my_math.cpp └── tests/ ├── CMakeLists.txt └── test_my_math.cpp

4.1 主CMakeLists.txt配置

在主目录的CMakeLists.txt中,你需要启用测试,并添加子目录。

cmake_minimum_required(VERSION 3.16) project(MyAwesomeProject VERSION 1.0.0 LANGUAGES CXX) # 设置C++标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 添加可执行文件(你的主程序或库) add_library(my_math src/my_math.cpp) target_include_directories(my_math PUBLIC include/) # 启用测试功能。这行命令必须放在定义测试之前。 enable_testing() # 添加包含测试的子目录 add_subdirectory(tests)

4.2 测试目录的CMakeLists.txt配置

tests/CMakeLists.txt中,我们使用CMake自带的FetchContent模块来动态获取并编译gtest。这是目前CMake官方推荐的方式,它避免了要求用户提前系统级安装gtest,实现了项目的自包含。

# 引入FetchContent模块 include(FetchContent) # 声明googletest的源码仓库信息 FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.14.0 # 指定一个稳定版本标签,推荐使用 ) # 如果未加载过,则执行获取和配置 FetchContent_MakeAvailable(googletest) # 添加你的测试可执行文件 add_executable(run_unit_tests test_my_math.cpp) # 链接你的主库和gtest库 target_link_libraries(run_unit_tests PRIVATE my_math GTest::gtest_main ) # 将可执行文件注册为测试用例,名为 “UnitTests” add_test(NAME UnitTests COMMAND run_unit_tests)

关键点解析

  1. FetchContent: 这个模块会在配置阶段(cmake命令执行时)自动下载指定版本的googletest源码到构建目录,并编译它。你的项目源码中不需要包含gtest的代码。
  2. GIT_TAG: 强烈建议指定一个具体的发布版本标签(如v1.14.0),而不是默认的main分支。这能确保构建的可重复性,避免因gtest主分支的更新导致你的项目突然构建失败。
  3. GTest::gtest_main: 这是由FetchContent_MakeAvailable(googletest)导入的CMake目标(target)。直接链接这个目标,CMake会自动处理好所有头文件路径、库文件链接以及必要的编译定义(如-pthread),你完全不需要手动指定-lgtest-pthread等。这是现代CMake“目标导向”用法的优势。
  4. add_test: 这条命令将run_unit_tests这个可执行文件注册到CTest(CMake的测试驱动器)中。之后你不仅可以直接运行./run_unit_tests,还可以在构建目录下使用ctestmake test命令来运行所有注册过的测试,ctest会提供更统一的测试运行报告。

4.3 编写测试代码

tests/test_my_math.cpp的内容示例:

#include “gtest/gtest.h” #include “my_math.h” // 你的项目头文件 TEST(MyMathTest, AddTest) { EXPECT_EQ(add(1, 2), 3); EXPECT_NE(add(1, 2), 4); EXPECT_LT(add(-1, -2), 0); } TEST(MyMathTest, DeathTest) { // 测试可能导致程序退出的条件,例如除零 ASSERT_DEATH({ int x = 1 / 0; }, “”); }

4.4 构建与运行测试

在你的项目根目录下:

mkdir build && cd build cmake .. make -j$(nproc)

编译完成后,你有两种方式运行测试:

  1. 直接运行测试程序./tests/run_unit_tests
  2. 使用CTest运行ctestmake test。使用ctest -V可以获得更详细的输出。

使用FetchContent的方式,使得你的项目在任何一台装有git、cmake和编译器的机器上都能一键完成依赖下载、编译和测试,极大地提升了项目的可移植性和协作效率。

5. 常见问题、疑难排查与进阶技巧

即使按照步骤操作,你也可能会遇到一些坑。这里我总结了一些常见问题及其解决方案。

5.1 编译或链接错误汇总

错误信息可能原因解决方案
fatal error: gtest/gtest.h: No such file or directory编译器找不到gtest头文件。1. 确认已执行sudo make install将头文件安装到/usr/local/include
2. 编译时使用-I选项指定头文件路径,如-I/usr/local/include
3. 在CMake项目中,检查target_include_directories是否正确链接了GTest::gtest目标。
undefined reference to ‘testing::…’链接器找不到gtest的库文件。1. 确认已执行sudo make installsudo ldconfig
2. 编译时使用-L指定库路径(如-L/usr/local/lib)并用-l链接库(如-lgtest -lgtest_main)。
3.确保添加了-pthread链接选项
undefined reference to ‘pthread_…’缺少POSIX线程库链接。在编译命令末尾明确加上-pthread。在CMake中,链接GTest::gtest目标会自动处理。
CMake Error at … FindGTest.cmakeCMake找不到系统安装的GTest。如果你选择用系统包安装(libgtest-dev),CMake的find_package(GTest)可能因为库文件未编译而失败。建议改用源码编译安装FetchContent方案。
运行测试时崩溃或输出乱码动态库链接问题。运行ldd ./your_test_program查看可执行文件依赖的库。确保libgtest.so的路径(如/usr/local/lib)在LD_LIBRARY_PATH环境变量中,或者已通过ldconfig注册。

5.2 动态库路径问题详解

这是Linux下安装本地库后最常见的问题。当你运行自己编译的程序时,系统动态链接器(ld.so)需要知道去哪里找libgtest.so

  • 检查依赖:使用ldd命令。

    ldd ./hello_test | grep gtest

    如果输出显示libgtest.so => not found,说明链接器没找到。

  • 解决方案

    1. 永久方案(推荐):我们之前执行的sudo ldconfig就是为了刷新系统库缓存。它读取/etc/ld.so.conf文件和/etc/ld.so.conf.d/目录下的配置,将配置的目录(包括/usr/local/lib)中的库文件信息缓存起来。执行后通常就能解决。
    2. 临时方案:在运行程序前,设置LD_LIBRARY_PATH环境变量。
      export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH ./hello_test
      这只对当前终端会话有效。
    3. 编译时方案:在编译时通过-Wl,-rpath选项将库路径嵌入可执行文件。
      g++ ... -Wl,-rpath=/usr/local/lib -lgtest ...
      这样程序运行时会自动去指定路径寻找库。

5.3 进阶使用技巧与心得

  1. 选择静态库还是动态库?

    • 在最初使用cmake配置时,通过-DBUILD_SHARED_LIBS=OFF可以编译静态库。
    • 静态库优点:部署简单,测试程序一个文件搞定,不依赖外部环境。适合在CI/CD流水线中运行测试,环境干净。
    • 静态库缺点:每个测试程序都包含一份gtest代码,体积大。如果gtest有安全更新,你需要重新编译所有测试程序。
    • 我的选择:在开发机上用动态库,方便。在发布测试环境或Docker镜像中,可以考虑使用静态库,减少依赖。
  2. 使用GMock进行模拟测试Google Mock(gmock)是gtest的一部分,用于做模拟(Mock)和打桩(Stub)。当你测试的模块依赖外部服务或复杂对象时,gmock无比强大。安装和链接方式与gtest完全一样,只需在代码中包含gmock/gmock.h,并链接-lgmock(或CMake目标GMock::gmock)。

  3. 让测试输出更友好

    • 运行测试时使用--gtest_color=yes可以开启彩色输出。
    • 使用--gtest_filter=*TestPattern*可以过滤只运行特定测试用例,例如--gtest_filter=MyMathTest.*只运行MyMathTest下的所有测试。
    • 使用--gtest_repeat=1000 --gtest_break_on_failure可以重复运行测试1000次,并在第一次失败时停止,用于排查偶发性错误。
  4. 在CLion/VSCode等IDE中集成在IDE中配置CMake项目时,确保CMake能正确找到gtest。使用FetchContent方案是兼容性最好的。在CLion中,它会被自动识别,测试用例旁边会出现绿色的运行按钮,可以直接点击运行单个测试,体验极佳。

  5. 一个我踩过的坑:版本兼容性曾经在一个老项目中,代码使用了gtest 1.8.x的API,而我的系统安装了1.11.x。某些内部API发生了变化,导致编译失败。教训是:对于重要项目,最好在项目内部通过FetchContent锁定一个特定的gtest版本(如v1.10.0),而不是依赖系统全局安装的、可能变化的版本。这保证了所有开发者以及构建服务器环境的一致性。

安装和配置gtest的过程,本质上是在学习如何管理一个C++项目的第三方依赖。从简单的apt-get到源码编译,再到现代CMake的FetchContent,每一步都对应着不同的工程化思维。掌握它,你收获的不仅仅是一个测试框架,更是一套在Linux环境下进行专业C++开发的构建与依赖管理方法论。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询