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

相关推荐
Rabitebla39 分钟前
【Linux 底层原理】进程(十):进程等待详解---从僵尸进程到 wait、waitpid 与 status
linux·c++·笔记·学习·visual studio
西西弗Sisyphus3 小时前
C++ 超时互斥锁(Timed Mutex):std::timed_mutex 与 std::unique_lock
c++
今天要早睡_5 小时前
C++01_入门基础复习
开发语言·c++
Fuyo_11198 小时前
C++ 运行时多态与虚函数机制全面剖析【面试重点】
开发语言·c++·面试
Chen_harmony9 小时前
七、STL与string类
开发语言·c++
xxxiugou12310 小时前
C++命名空间与引用全解析
开发语言·c++
用户76241175670511 小时前
第十五章 人工智能基础
c++
爱写Bug的浮游13 小时前
unique_ptr 零开销的真相与边界
c++·facebook
xiaoye-duck14 小时前
《C++ 项目:手写大模型接入 SDK》ChatGPT接口设计(下):流式响应实现和测试
网络·c++·语言模型·chatgpt
落樱弥城15 小时前
Perf 逐步探查性能缺口并优化
c++·性能优化