news 2026/2/2 13:11:48

四种常见的代码覆盖率测试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
四种常见的代码覆盖率测试

您听说过“代码覆盖率”吗?在这篇文章中,我们将探讨什么是测试中的代码覆盖率,以及四种衡量它的常用方法。

什么是代码覆盖率

代码覆盖率是衡量测试代码测试了源代码百分比多少的指标。它可以帮助您识别可能缺乏适当测试的代码区域。

通常,覆盖率指标会这样去记录:

File% Statements% Branch% Functions% LinesUncovered lines
file.js90%100%90%80%89,256
coffee.js55.55%80%50%62.5%10-11, 18

当您添加新的功能和测试时,增加代码覆盖率百分比可以让您更加确信您的应用程序已经经过了彻底的测试。然而,还有更多的问题有待发现。

四种常见的代码覆盖类型

有四种常见的方法来收集和计算代码覆盖率:函数、行、分支和语句覆盖率。要查看每种类型的代码覆盖率如何计算其百分比,请思考以下计算咖啡成分的代码示例:

  1. /* coffee.js */

  2. export function calcCoffeeIngredient(coffeeName, cup = 1) {

  3. let espresso, water;

  4. if (coffeeName === 'espresso') {

  5. espresso = 30 * cup;

  6. return { espresso };

  7. }

  8. if (coffeeName === 'americano') {

  9. espresso = 30 * cup; water = 70 * cup;

  10. return { espresso, water };

  11. }

  12. return {};

  13. }

  14. export function isValidCoffee(name) {

  15. return ['espresso', 'americano', 'mocha'].includes(name);

  16. }

不是很懂英语,去查了一下分别是:espresso-浓缩咖啡,americano-美式咖啡,mocha-摩卡咖啡

验证calcCoffeeIngredient函数的测试是

  1. /* coffee.test.js */

  2. import { describe, expect, assert, it } from 'vitest';

  3. import { calcCoffeeIngredient } from '../src/coffee-incomplete';

  4. describe('Coffee', () => {

  5. it('should have espresso', () => {

  6. const result = calcCoffeeIngredient('espresso', 2);

  7. expect(result).to.deep.equal({ espresso: 60 });

  8. });

  9. it('should have nothing', () => {

  10. const result = calcCoffeeIngredient('unknown');

  11. expect(result).to.deep.equal({});

  12. });

  13. });

您可以在此demo中运行代码和测试,也可以签出存储库。

函数覆盖率

代码覆盖率:50%

  1. /* coffee.js */

  2. export function calcCoffeeIngredient(coffeeName, cup = 1) {

  3. // ...

  4. }

  5. function isValidCoffee(name) {

  6. // ...

  7. }

功能覆盖率是一个简单的指标。它表示计算出测试代码调用了源代码中百分之多少函数。

在代码示例中,有两个函数:calcCoffeeIngredientisValidCoffee。测试代码只调用calcCoffeeIngredient函数,因此函数覆盖率为50%。

行覆盖率

代码覆盖率:62.5%

  1. /* coffee.js */

  2. export function calcCoffeeIngredient(coffeeName, cup = 1) {

  3. let espresso, water;

  4. if (coffeeName === 'espresso') { // 1

  5. espresso = 30 * cup; // 2

  6. return { espresso }; // 3

  7. }

  8. if (coffeeName === 'americano') { // 4

  9. espresso = 30 * cup; water = 70 * cup; // 5

  10. return { espresso, water }; // 6

  11. }

  12. return {}; // 7

  13. }

  14. export function isValidCoffee(name) {

  15. return ['espresso', 'americano', 'mocha'].includes(name); // 8

  16. }

行覆盖率表示测试代码覆盖源代码的可执行代码行的百分比。如果一行代码仍然未执行,这意味着代码的某些部分没有经过测试。

代码示例有8行可执行代码,但是测试不执行americano条件(两行)和isValidCoffee函数(一行)。这使得线路覆盖率达到62.5%。

分支覆盖率

代码覆盖率:80%

  1. /* coffee.js */

  2. export function calcCoffeeIngredient(coffeeName, cup = 1) {

  3. // ...

  4. if (coffeeName === 'espresso') {

  5. // ...

  6. return { espresso };

  7. }

  8. if (coffeeName === 'americano') {

  9. // ...

  10. return { espresso, water };

  11. }

  12. return {};

  13. }

分支覆盖率表示代码中执行分支或决策点的百分比,例如if语句或循环。它测定测试是否检查条件语句为true和false的分支。

代码示例中有五个分支:

  1. 只使用coffeeName调用calccoffeingredient
  2. coffeeNamecup调用calcCoffeeIngredient
  3. coffeeName是 浓缩咖啡 √
  4. coffeeName是 美式 ×
  5. 其他咖啡 √

测试涵盖除了咖啡是美式咖啡条件所有分支,所以分支覆盖率是80%。

语句覆盖率

代码覆盖率:55.55%

  1. /* coffee.js */

  2. export function calcCoffeeIngredient(coffeeName, cup = 1) {

  3. let espresso, water;

  4. if (coffeeName === 'espresso') {

  5. espresso = 30 * cup;

  6. return { espresso };

  7. }

  8. if (coffeeName === 'americano') {

  9. espresso = 30 * cup; water = 70 * cup;

  10. return { espresso, water };

  11. }

  12. return {};

  13. }

  14. export function isValidCoffee(name) {

  15. return ['espresso', 'americano', 'mocha'].includes(name);

  16. }

语句覆盖率检测测试代码执行了代码中百分之几的语句。乍一看,您可能会想,这与线路覆盖不一样吗?实际上,语句覆盖类似于行覆盖,但考虑的是包含多个语句的单行代码。

在代码示例中,有8行可执行代码,但是有9条语句。你能找出包含两个语句的行吗?

答案揭晓:一行有两个语句的代码:espresso = 30 * cup; water = 70 * cup;

测试只覆盖了9条语句中的5条,因此语句覆盖率为55.55%。

如果您总是每行写一条语句,那么您的行覆盖率将与语句覆盖率相似。

您应该选择哪种类型的代码覆盖率?

大多数代码覆盖率测试工具包括这四种类型的通用代码覆盖率。选择哪个代码覆盖指标来确定优先级取决于具体的项目需求、开发实践和测试目标。

通常,语句覆盖率是一个很好的起点,因为它是一个简单且易于理解的指标。与语句覆盖率不同,分支覆盖率和函数覆盖率指标的是测试是调用条件(分支)还是函数。因此,它们是语句覆盖之后才应该考虑的。

一旦您获得了较高的语句覆盖率,您就可以继续进行提高分支覆盖率和函数覆盖率。

测试覆盖率与代码覆盖率相同吗

不一样。测试覆盖率和代码覆盖率经常被混淆,但它们是不同的:

  • 测试覆盖率:一种度量测试套件覆盖软件特性的程度的定性度量。它有助于确定所涉及的风险水平。
  • 代码覆盖率:一种量化的度量,用于度量在测试期间执行的代码的比例。它是关于测试覆盖了多少代码。

这里有一个简单的类比:把一个web应用程序想象成一个房子。

  • 测试覆盖率衡量房子里覆盖了多少房间的程度。
  • 代码覆盖率衡量了测试了多少房子。

100%的代码覆盖率并不意味着没有bug

虽然在测试中实现高代码覆盖率当然是可取的,但100%的代码覆盖率并不能保证代码中没有错误或缺陷。

实现100%代码覆盖率的毫无意义的方法

参考下面的测试:

  1. /* coffee.test.js */

  2. // ...

  3. describe('Warning: Do not do this', () => {

  4. it('is meaningless', () => {

  5. calcCoffeeIngredient('espresso', 2);

  6. calcCoffeeIngredient('americano');

  7. calcCoffeeIngredient('unknown');

  8. isValidCoffee('mocha');

  9. expect(true).toBe(true); // not meaningful assertion

  10. });

  11. });

这个测试实现了100%的函数、行、分支和语句覆盖率,但是它没有意义,因为它实际上并没有测试代码。无论代码是否正常工作,expect(true).tobe(true)断言都会通过测试。

一个糟糕的度量标准比没有度量标准更糟糕

一个糟糕的指标会给你一种虚假的安全感,这比根本没有指标更糟糕。例如,如果您有一个达到100%代码覆盖率的测试代码,但是所有的测试都是无意义的,那么您可能会有一种错误的安全感,认为您的代码已经经过了很好的测试。如果不小心删除或破坏了应用程序代码的一部分,即使应用程序不再正常工作,测试仍然会通过。

要避免这种情况:

  • 测试评估:编写和审查测试,以确保它们是有意义的,并在各种不同的场景中测试代码。
  • 使用代码覆盖率作为指导方针,而不是作为测试有效性或代码质量的唯一度量。

在不同类型的测试中使用代码覆盖率

让我们仔细看看如何使用三种常见类型的测试的代码覆盖率:

  • 单元测试。它们是收集代码覆盖率的最佳测试类型,因为它们被设计为覆盖多个小场景和测试路径。
  • 集成测试。它们可以帮助收集集成测试的代码覆盖率,但是要谨慎使用。在这种情况下,您计算了源代码的大部分的覆盖率,并且很难确定哪些测试实际上覆盖了代码的哪些部分。尽管如此,计算集成测试的代码覆盖率对于没有良好隔离单元的遗留系统可能是有用的。
  • 端到端(E2E)测试。由于这些测试的复杂性,测量E2E测试的代码覆盖率是困难和具有挑战性的。所以不应该使用代码覆盖,更好的方法是选择需求覆盖。这是因为E2E测试的重点是覆盖测试的需求,而不是关注源代码。

总结

代码覆盖率是衡量测试有效性的有用指标。通过确保代码中的关键逻辑经过良好测试,它可以帮助您提高应用程序的质量。

然而,请记住代码覆盖率只是一个度量标准。确保还要考虑其他因素,例如测试的质量和应用程序需求。

100%的代码覆盖率不是目标。相反,您应该使用代码覆盖以及一个包含各种测试方法的全面测试计划,包括单元测试、集成测试、端到端测试。

请参阅完整的代码示例,并使用良好的代码覆盖率进行测试。您还可以使用这个demo运行代码和测试。

  1. /* coffee.js - a complete example */

  2. export function calcCoffeeIngredient(coffeeName, cup = 1) {

  3. if (!isValidCoffee(coffeeName)) return {};

  4. let espresso, water;

  5. if (coffeeName === 'espresso') {

  6. espresso = 30 * cup;

  7. return { espresso };

  8. }

  9. if (coffeeName === 'americano') {

  10. espresso = 30 * cup; water = 70 * cup;

  11. return { espresso, water };

  12. }

  13. throw new Error (`${coffeeName} not found`);

  14. }

  15. function isValidCoffee(name) {

  16. return ['espresso', 'americano', 'mocha'].includes(name);

  17. }

  1. /* coffee.test.js - a complete test suite */

  2. import { describe, expect, it } from 'vitest';

  3. import { calcCoffeeIngredient } from '../src/coffee-complete';

  4. describe('Coffee', () => {

  5. it('should have espresso', () => {

  6. const result = calcCoffeeIngredient('espresso', 2);

  7. expect(result).to.deep.equal({ espresso: 60 });

  8. });

  9. it('should have americano', () => {

  10. const result = calcCoffeeIngredient('americano');

  11. expect(result.espresso).to.equal(30);

  12. expect(result.water).to.equal(70);

  13. });

  14. it('should throw error', () => {

  15. const func = () => calcCoffeeIngredient('mocha');

  16. expect(func).toThrowError(new Error('mocha not found'));

  17. });

  18. it('should have nothing', () => {

  19. const result = calcCoffeeIngredient('unknown')

  20. expect(result).to.deep.equal({});

  21. });

  22. });

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/1 11:08:16

从微调到生产:用Llama Factory构建端到端AI流水线

从微调到生产:用Llama Factory构建端到端AI流水线 在AI模型开发过程中,从实验阶段的微调到生产环境的部署往往需要跨越多个工具链和技术栈。Llama Factory作为一个集成化解决方案,能够帮助工程团队建立标准化的AI流程,覆盖从数据准…

作者头像 李华
网站建设 2026/1/30 2:32:23

Sambert-HifiGan中文语音合成的音色克隆技术

Sambert-HifiGan中文语音合成的音色克隆技术 📌 技术背景与核心价值 在智能语音交互、虚拟人、有声内容生成等场景中,自然、富有情感的中文语音合成(TTS) 正成为关键能力。传统的TTS系统往往语音单调、缺乏表现力,难…

作者头像 李华
网站建设 2026/1/31 6:00:50

灰度测试是什么?

灰度测试是什么? 灰度测试是软件测试过程中的一种测试方法,结合了黑盒测试和白盒测试的特点。在灰度测试中,测试人员对系统的内部结构、设计和实现有一定的了解,但不完全了解所有的细节。 灰度测试是基于软件要求和设计文档进行…

作者头像 李华
网站建设 2026/1/29 19:03:48

Sambert-HifiGan语音合成在AR/VR中的应用

Sambert-HifiGan 中文多情感语音合成在 AR/VR 中的应用 引言:语音合成如何赋能下一代沉浸式体验? 随着增强现实(AR)与虚拟现实(VR)技术的快速发展,用户对自然、拟人化的人机交互方式提出了更高要…

作者头像 李华
网站建设 2026/1/29 20:09:23

基于python和Vue的新能源汽车租赁管理系统的设计与实现_rtg8b209

目录新能源汽车租赁管理系统的设计与实现开发技术核心代码参考示例1.建立用户稀疏矩阵,用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度总结源码文档获取/同行可拿货,招校园代理 :文章底部获取博主联系方式!新能源汽车租赁…

作者头像 李华
网站建设 2026/1/29 17:53:56

nodejs+uniapp+vue微信小程序的班级课堂考勤学生签到系统_1ux1fd8x

文章目录系统概述技术架构核心功能模块创新点与优化应用场景与扩展性主要技术与实现手段系统设计与实现的思路系统设计方法java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式!系统概述 该系统基于Node.js后端、Un…

作者头像 李华