代码覆盖率:gcov工具介绍、覆盖率类型、覆盖率报告生成

说到单元测试,有个话题绕不开——代码覆盖率

我刚开始做嵌入式开发那会儿,对覆盖率其实没啥概念。觉得测试跑通了就行,管它覆盖了多少代码呢?结果有一次,一个看似完美的测试用例,愣是没发现某个条件分支里的野指针。产品上线后,客户现场偶发死机,查了整整两周才定位到问题。嗯,从那以后,我再也不敢忽视覆盖率了。

什么是代码覆盖率?

说白了,覆盖率就是衡量你的测试用例到底跑了多少代码的一个指标。

你写了一个函数,里面有 if-else、for 循环、switch-case。你的测试用例执行了哪些行?哪些分支走了?哪些条件组合没测到?覆盖率就是回答这些问题的。

核心观点:覆盖率不是越高越好,但太低一定有问题。我个人习惯,单元测试覆盖率至少做到 80% 以上,关键模块要求 90%+。

覆盖率类型

覆盖率不是只有一种。不同维度看代码,有不同的覆盖率指标。我整理了一下,常见的就这几种:

覆盖率类型 含义 典型场景
语句覆盖率 每行代码是否被执行过 最基础,必须达标
分支覆盖率 每个 if/else 分支是否都走过 条件判断逻辑多的函数
条件覆盖率 每个布尔子表达式取 true/false 都测到 复杂逻辑表达式
MC/DC 覆盖率 每个条件独立影响结果 安全关键系统(如汽车、航空)
函数覆盖率 每个函数是否被调用过 检查有没有死代码

你想想看,如果你的测试只覆盖了语句,分支没走全,那隐藏的 bug 可能就藏在那个没测到的 else 里。我遇到过好几次这种情况,所以现在写测试时,我会刻意检查分支覆盖率。

gcov 工具介绍

gcov 是 GCC 自带的代码覆盖率工具。不需要额外安装,只要你的编译器是 GCC 系列,就能用。它配合 gcov 命令和 -fprofile-arcs -ftest-coverage 编译选项,就能生成覆盖率数据。

怎么用?我直接给你看步骤:

  1. 编译时加选项gcc -fprofile-arcs -ftest-coverage -o test_program test.c
  2. 运行程序./test_program,会生成 .gcda.gcno 文件
  3. 生成报告gcov test.c,会输出 test.c.gcov 文件

打开 .gcov 文件,你会看到每行代码前面有个数字,表示该行被执行了多少次。如果数字是 #####,说明这行代码从未被执行

小技巧:我习惯用 gcov -b 选项,它会额外输出分支覆盖率信息。这样一眼就能看出哪些分支没走到。

覆盖率报告生成

光看 .gcov 文本文件,说实话不太直观。尤其是项目大了以后,几百个源文件,一个个翻太痛苦了。

我推荐用 lcov 工具,它能把 gcov 的输出转换成 HTML 报告,带颜色、带进度条,一目了然。

基本用法:

# 1. 编译并运行测试(生成 .gcda 文件)
gcc -fprofile-arcs -ftest-coverage -o test test.c
./test

# 2. 用 lcov 收集覆盖率数据
lcov --capture --directory . --output-file coverage.info

# 3. 生成 HTML 报告
genhtml coverage.info --output-directory coverage_report

打开 coverage_report/index.html,你会看到每个文件的覆盖率百分比,点进去还能看到具体哪些行被覆盖、哪些没覆盖。红色表示未覆盖,绿色表示已覆盖。

注意:lcov 默认会包含系统头文件(比如 /usr/include 下的内容)。建议用 --extract--remove 选项过滤掉,只保留你自己的源码。否则报告会变得很乱。

知识体系图

下面这张图,我把本章的核心内容串了一下。你可以把它当成一个快速索引:

代码覆盖率知识体系 代码覆盖率 覆盖率类型 语句覆盖率 分支覆盖率 条件覆盖率 MC/DC 函数覆盖率 gcov 工具 编译选项:-fprofile-arcs -ftest-coverage 生成 .gcda / .gcno 文件 报告生成 lcov 收集 → genhtml 生成 HTML 红色=未覆盖,绿色=已覆盖

避坑指南

讲几个我踩过的坑,你遇到了可以少走弯路:

  • gcov 文件没生成? 检查一下编译选项是不是加对了。我遇到过好几次,编译时忘了加 -fprofile-arcs,结果跑完测试啥也没生成。
  • 覆盖率数据不准? 可能是你之前编译的残留文件没清理。建议每次重新编译前,先 make clean 或者手动删掉 .gcda 文件。
  • 嵌入式交叉编译怎么用? 在目标板上运行测试,然后把 .gcda 文件拷回开发机,再用 gcov 分析。注意交叉编译器的 gcov 版本要和开发机的一致,否则可能解析失败。
  • 不要只看语句覆盖率。 我曾经有一个模块语句覆盖率 95%,但分支覆盖率只有 60%。结果那个没测到的分支,正好是异常处理逻辑。上线后出问题,查了半天才发现。

我的习惯:每次提交代码前,我会跑一遍覆盖率报告,重点看两个地方:一是新增代码的覆盖率,二是之前覆盖率低的老代码有没有改善。这样能保证代码质量持续提升。

好了,关于代码覆盖率,核心就是这些。gcov 是个好工具,但工具只是辅助,关键还是你得有覆盖率意识。写测试的时候,多想想:我这个用例,到底覆盖了哪些代码?还有哪些分支没走到?

公众号:蓝海资料掘金营,微信deep3321