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"。

相关推荐
郝学胜_神的一滴2 天前
CMake 034:生成器表达式:解耦构建时序、精简分支逻辑的终极利器
c++·cmake
见过夏天2 天前
C++ 基础入门完全指南
c++
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK4 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境5 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境5 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴6 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
卷无止境8 天前
C++ 的Eigen 库全解析
c++
卷无止境8 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴8 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake