GoogleTest 使用指南 | 单元覆盖率分析

GoogleTest 使用指南 | 单元覆盖率分析

  • [GoogleTest 使用指南 | 单元测试覆盖率分析](#GoogleTest 使用指南 | 单元测试覆盖率分析)

GoogleTest 使用指南 | 单元测试覆盖率分析

除了查看测试通过与否,分析测试覆盖率也是提升代码质量的重要手段。覆盖率分析工具可以帮助识别未被测试的代码路径。

以下演示的是 Apple 芯片的 MacBook 上安装 GCC 版本 lcov 进行覆盖率分析。

安装 GCC:

bash 复制代码
brew install gcc

安装 GCC 编译覆盖率版本的 lcov:

bash 复制代码
brew install gcc lcov

查看 GCC 版本:

bash 复制代码
➜  googletest-example git:(master) ls /opt/homebrew/bin/gcc-*
/opt/homebrew/bin/gcc-15        /opt/homebrew/bin/gcc-ar-15     /opt/homebrew/bin/gcc-nm-15     /opt/homebrew/bin/gcc-ranlib-15

➜  googletest-example git:(master) ls /opt/homebrew/bin/g++-*
/opt/homebrew/bin/g++-15

配置覆盖率构建目录:

bash 复制代码
cmake -S . -B build-coverage \
    -DCMAKE_BUILD_TYPE=Debug \
    -DCMAKE_C_COMPILER="$(brew --prefix)/bin/gcc-15" \
    -DCMAKE_CXX_COMPILER="$(brew --prefix)/bin/g++-15" \
    -DCMAKE_C_FLAGS="--coverage -O0 -g" \
    -DCMAKE_CXX_FLAGS="--coverage -O0 -g" \
    -DCMAKE_EXE_LINKER_FLAGS="--coverage"

输出:

复制代码
Test project /Users/xiye/CppProjects/googletest-example/build-coverage
      Start  1: test_main
 1/14 Test  #1: test_main ........................   Passed    0.60 sec
      Start  2: test_max
 2/14 Test  #2: test_max .........................   Passed    0.35 sec
      Start  3: test_min
 3/14 Test  #3: test_min .........................   Passed    0.36 sec
      Start  4: test_prime
 4/14 Test  #4: test_prime .......................   Passed    0.36 sec
      Start  5: test_suite
 5/14 Test  #5: test_suite .......................   Passed    0.36 sec
      Start  6: test_calculator
 6/14 Test  #6: test_calculator ..................   Passed    0.37 sec
      Start  7: test_container
 7/14 Test  #7: test_container ...................   Passed    0.34 sec
      Start  8: test_numeric
 8/14 Test  #8: test_numeric .....................   Passed    0.41 sec
      Start  9: test_queue
 9/14 Test  #9: test_queue .......................   Passed    0.35 sec
      Start 10: test_bank_vault
10/14 Test #10: test_bank_vault ..................   Passed    0.34 sec
      Start 11: test_utility
11/14 Test #11: test_utility .....................   Passed    0.35 sec
      Start 12: test_database
12/14 Test #12: test_database ....................   Passed    0.36 sec
      Start 13: test_add_param
13/14 Test #13: test_add_param ...................   Passed    0.36 sec
      Start 14: test_network_client
14/14 Test #14: test_network_client ..............   Passed    0.37 sec

100% tests passed, 0 tests failed out of 14

Total Test time (real) =   5.32 sec

构建并运行测试:

bash 复制代码
cmake --build build-coverage
lcov --directory build-coverage --zerocounters
ctest --test-dir build-coverage --output-on-failure

收集覆盖率:

bash 复制代码
lcov --capture \
    --directory build-coverage \
    --gcov-tool /opt/homebrew/bin/gcov-15 \
    --ignore-errors inconsistent \
    --output-file build-coverage/coverage.info

过滤第三方库、测试代码、构建目录:

bash 复制代码
lcov --remove build-coverage/coverage.info \
    "*/_deps/*" \
    "*/tests/*" \
    "/Applications/*" \
    "/Library/*" \
    "/opt/homebrew/*" \
    --gcov-tool /opt/homebrew/bin/gcov-15 \
    --ignore-errors unused \
    --output-file build-coverage/coverage.filtered.info

生成 HTML:

bash 复制代码
genhtml build-coverage/coverage.filtered.info \
    --output-directory build-coverage/coverage-report

打开报告:

bash 复制代码
open build-coverage/coverage-report/index.html

结果:

打开单个文件,可以查看每一行的覆盖情况:

可以在 CMakeLists.txt 中添加覆盖率编译选项,由 ENABLE_COVERAGE 开关控制。

txt 复制代码
option(ENABLE_COVERAGE "Enable coverage flags" OFF)

function(enable_coverage target)
	if(ENABLE_COVERAGE)
		target_compile_options(${target} PRIVATE --coverage -O0 -g)
		target_link_options(${target} PRIVATE --coverage)
	endif()
endfunction()

然后在每个目标创建后调用它,例如你的项目里可以这样:

txt 复制代码
  add_library(example_lib
      src/calculator.cpp
      src/max.cpp
  )
  target_include_directories(example_lib PUBLIC
      ${CMAKE_CURRENT_SOURCE_DIR}/include
  )
  enable_coverage(example_lib)

  add_executable(main src/main.cpp)
  target_link_libraries(main PRIVATE example_lib)
  enable_coverage(main)

你的测试函数也可以改成:

txt 复制代码
  function(add_gtest_target target)
      add_executable(${target} ${ARGN})
      target_link_libraries(${target} PRIVATE example_lib gtest_main)
      enable_coverage(${target})
      add_test(NAME ${target} COMMAND ${target})
  endfunction()

单独写的测试目标也要加:

txt 复制代码
  add_executable(test_network_client tests/test_network_client.cpp)
  target_link_libraries(test_network_client PRIVATE example_lib gmock_main)
  enable_coverage(test_network_client)
  add_test(NAME test_network_client COMMAND test_network_client)

之后用 GCC 配置覆盖率构建:

bash 复制代码
  cmake -S . -B build-coverage \
    -DCMAKE_BUILD_TYPE=Debug \
    -DCMAKE_C_COMPILER=/opt/homebrew/bin/gcc-15 \
    -DCMAKE_CXX_COMPILER=/opt/homebrew/bin/g++-15 \
    -DENABLE_COVERAGE=ON

再构建和测试:

bash 复制代码
  cmake --build build-coverage
  ctest --test-dir build-coverage --output-on-failure

这样覆盖率选项就由 CMake 管理,不需要每次手动传 CMAKE_CXX_FLAGS="--coverage -O0 -g"。

相关推荐
星恒随风20 分钟前
C++ 类和对象入门(五):初始化列表、explicit 和 static 成员详解
开发语言·c++·笔记·学习·状态模式
浪客灿心1 小时前
项目篇:模块设计与实现
数据库·c++
牛油果子哥q1 小时前
【C++ STL vector】C++ STL vector 终极精讲:动态数组底层原理、两倍扩容机制、迭代器失效、增删查改、性能剖析与工程避坑指南
开发语言·c++
测试员周周2 小时前
【AI测试智能体-面试】AI测试面试60题(附回答思路)
人工智能·python·功能测试·测试工具·单元测试·自动化·测试用例
为何创造硅基生物3 小时前
独占指针的创建std::make_unique 本身自带堆出现
c++
kyle~3 小时前
ROS 2 与 Isaac Sim 联合仿真(一)体系架构、环境选型与基础通信闭环
c++·机器人·nvidia·仿真·ros2
努力努力再努力wz4 小时前
【内存管理与高并发内存池系列】从 mmap 到 malloc:文件映射、匿名映射与 glibc 内存分配机制详解
linux·c语言·数据结构·数据库·c++·qt·链表
八解毒剂4 小时前
数据结构-平衡二叉树——对二叉搜索树的优化
数据结构·c++·算法
起床困难户5754 小时前
条款20:协助完成返回值优化
c++
啦啦啦啦啦zzzz5 小时前
算法总结(二分查找、双指针)
c++·算法