代码覆盖率: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 编译选项,就能生成覆盖率数据。
怎么用?我直接给你看步骤:
- 编译时加选项:
gcc -fprofile-arcs -ftest-coverage -o test_program test.c - 运行程序:
./test_program,会生成.gcda和.gcno文件 - 生成报告:
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 选项过滤掉,只保留你自己的源码。否则报告会变得很乱。
知识体系图
下面这张图,我把本章的核心内容串了一下。你可以把它当成一个快速索引:
避坑指南
讲几个我踩过的坑,你遇到了可以少走弯路:
- gcov 文件没生成? 检查一下编译选项是不是加对了。我遇到过好几次,编译时忘了加
-fprofile-arcs,结果跑完测试啥也没生成。 - 覆盖率数据不准? 可能是你之前编译的残留文件没清理。建议每次重新编译前,先
make clean或者手动删掉.gcda文件。 - 嵌入式交叉编译怎么用? 在目标板上运行测试,然后把
.gcda文件拷回开发机,再用 gcov 分析。注意交叉编译器的 gcov 版本要和开发机的一致,否则可能解析失败。 - 不要只看语句覆盖率。 我曾经有一个模块语句覆盖率 95%,但分支覆盖率只有 60%。结果那个没测到的分支,正好是异常处理逻辑。上线后出问题,查了半天才发现。
我的习惯:每次提交代码前,我会跑一遍覆盖率报告,重点看两个地方:一是新增代码的覆盖率,二是之前覆盖率低的老代码有没有改善。这样能保证代码质量持续提升。
好了,关于代码覆盖率,核心就是这些。gcov 是个好工具,但工具只是辅助,关键还是你得有覆盖率意识。写测试的时候,多想想:我这个用例,到底覆盖了哪些代码?还有哪些分支没走到?