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

1801 篇文章 51 订阅
575 篇文章 1 订阅

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

什么是代码覆盖率

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

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

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

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

四种常见的代码覆盖类型

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

/* coffee.js */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  let espresso, water;

  if (coffeeName === 'espresso') {
    espresso = 30 * cup;
    return { espresso };
  }

  if (coffeeName === 'americano') {
    espresso = 30 * cup; water = 70 * cup;
    return { espresso, water };
  }

  return {};
}

export function isValidCoffee(name) {
  return ['espresso', 'americano', 'mocha'].includes(name);
}

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

验证calcCoffeeIngredient函数的测试是

/* coffee.test.js */

import { describe, expect, assert, it } from 'vitest';
import { calcCoffeeIngredient } from '../src/coffee-incomplete';

describe('Coffee', () => {
  it('should have espresso', () => {
    const result = calcCoffeeIngredient('espresso', 2);
    expect(result).to.deep.equal({ espresso: 60 });
  });

  it('should have nothing', () => {
    const result = calcCoffeeIngredient('unknown');
    expect(result).to.deep.equal({});
  });
});

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

函数覆盖率

代码覆盖率:50%

/* coffee.js */

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

function isValidCoffee(name) {
  // ...
}

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

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

行覆盖率

代码覆盖率:62.5%

/* coffee.js */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  let espresso, water;

  if (coffeeName === 'espresso') { // 1
    espresso = 30 * cup;  // 2
    return { espresso };  // 3
  }

  if (coffeeName === 'americano') {  // 4
    espresso = 30 * cup; water = 70 * cup; // 5
    return { espresso, water };  // 6
  }

  return {};  // 7
}

export function isValidCoffee(name) {
  return ['espresso', 'americano', 'mocha'].includes(name);  // 8
}

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

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

分支覆盖率

代码覆盖率:80%

/* coffee.js */

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

  if (coffeeName === 'espresso') {
    // ...
    return { espresso };
  }

  if (coffeeName === 'americano') {
    // ...
    return { espresso, water };
  }

  return {};
}
…

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

代码示例中有五个分支:

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

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

语句覆盖率

代码覆盖率:55.55%

/* coffee.js */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  let espresso, water;

  if (coffeeName === 'espresso') {
    espresso = 30 * cup;
    return { espresso };
  }

  if (coffeeName === 'americano') {
    espresso = 30 * cup; water = 70 * cup;
    return { espresso, water };
  }

  return {};
}

export function isValidCoffee(name) {
  return ['espresso', 'americano', 'mocha'].includes(name);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

参考下面的测试:

/* coffee.test.js */

// ...
describe('Warning: Do not do this', () => {
  it('is meaningless', () => { 
    calcCoffeeIngredient('espresso', 2);
    calcCoffeeIngredient('americano');
    calcCoffeeIngredient('unknown');
    isValidCoffee('mocha');
    expect(true).toBe(true); // not meaningful assertion
  });
});

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

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

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

要避免这种情况:

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

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

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

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

总结

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

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

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

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

/* coffee.js - a complete example */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  if (!isValidCoffee(coffeeName)) return {};

  let espresso, water;

  if (coffeeName === 'espresso') {
    espresso = 30 * cup;
    return { espresso };
  }

  if (coffeeName === 'americano') {
    espresso = 30 * cup; water = 70 * cup;
    return { espresso, water };
  }

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

function isValidCoffee(name) {
  return ['espresso', 'americano', 'mocha'].includes(name);
}
/* coffee.test.js - a complete test suite */

import { describe, expect, it } from 'vitest';
import { calcCoffeeIngredient } from '../src/coffee-complete';

describe('Coffee', () => {
  it('should have espresso', () => {
    const result = calcCoffeeIngredient('espresso', 2);
    expect(result).to.deep.equal({ espresso: 60 });
  });

  it('should have americano', () => {
    const result = calcCoffeeIngredient('americano');
    expect(result.espresso).to.equal(30);
    expect(result.water).to.equal(70);
  });

  it('should throw error', () => {
    const func = () => calcCoffeeIngredient('mocha');
    expect(func).toThrowError(new Error('mocha not found'));
  });

  it('should have nothing', () => {
    const result = calcCoffeeIngredient('unknown')
    expect(result).to.deep.equal({});
  });
});

最后:下方这份完整的软件测试视频教程已经整理上传完成,需要的朋友们可以自行领取【保证100%免费】

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 一 软件测试 从零开始 5 1.1 引言 5 1.2 测试准备工作 5 1.2.1 向有经验测试人员学习 5 1.2.2 阅读软件测试的相关书籍 6 1.2.3 走读缺陷跟踪库中的问题报告单 6 1.2.4 走读相关产品的历史测试用例 6 1.2.5 学习产品相关的业务知识 6 1.3 识别测试需求 7 1.3.1 主动获取需求 7 1.3.2 确认需求的优先级 8 1.3.3 加入开发小组的邮件群组 8 1.3.4 与开发人员为邻 8 1.4 测试用例设计 8 1.4.1 测试用例的基本格式 8 1.4.2 重用同类型项目的测试用例 9 1.4.3 利用已有的软件 Checklist 9 1.4.4 加强测试用例的评审 10 1.4.5 定义测试用例的执行顺序 10 1.5 测试用例执行 10 1.5.1 搭建软件测试环境,执行测试用例 10 1.5.2 测试执行过程应注意的问题 11 1.5.3 及时更新测试用例 11 1.5.4 提交一份优秀的问题报告单 12 1.6 测试结果分析 12 1.7 总结 13 二 软件测试的常识 13 2.1 引言 13 2.2 软件测试常识 13 2.2.1 测试是不完全的(测试不完全) 13 2.2.2 测试具有免疫性(软件缺陷免疫性) 14 2.2.3 测试是 “ 泛型概念 ” (全程测试) 14 2.2.4 80-20 原则 14 2.2.5 为效益而测试 15 2.2.6 缺陷的必然性 15 2.2.7 软件测试必须有预期结果 15 2.2.8 软件测试的意义 - 事后分析 15 2.2.9 结论: 15 三 浅谈软件开发中的注意事项 16 3.1 项目设计 16 3.2 设计变化和需求变化 16 3.3 代码编写 17 3.3.1 源程序文件结构 17 3.3.2 界面设计风格的一致性 17 3.3.3 编辑风格 17 3.3.4 命名规范 18 3.4 BUG修补 18 3.5 开发人员的测试 18 四 软件测试的若干问题 19 4.1 前言 19 4.2 博弈的各方 19 4.3 测试的过程 20 4.4 测试所具备的素质 20 4.5 自动化测试 20 4.6 测试的误区 21 五 浅谈功能测试用例模板设计 21 5.1 Excel 模版 21 5.2 测试用例状态转换分析 23 六 如何提高软件质量 23 6.1 什么是质量 24 6.2 流程对质量的贡献 25 6.3 流程与技术 27 6.4 全面质量管理 28 6.5 关注测试 29 6.6 成功的铁三角 30 6.7 国际上流行的质量标准 30 6.8 如何起步 32 七 ISO和CMM,我们该选择谁 32 7.1 管理水平的适用性 33 7.2 复杂度的适用性 33 7.2.1何谓研发过程复杂度 34 7.2.2 何谓组织机构复杂度 34 7.3 量化管理的适用性上 35 7.4 结论 36 八 如何做好单元测试 36 8.1 前言 36 8.2 组织结构应该保证测试组参与单元测试 36 8.3 加强单元测试流程规范性 37 8.3.1 制订单元测试的过程定义 37 8.3.2 单元测试工作产品必须纳入配置管理 38 8.3.3 必须制订覆盖率指标和质量目标来指导和验收单元测试 38 8.3.4 加强详细设计文档评审 39 8.4 单元测试者技能的提高 39 8.4.1 加强对单元测试人员的技能培训 39 8.4.2 必须引入工具进行辅助 40 8.4.3 单元测试者加强对被测软件的全面了解 40 8.5 结尾 40 九 漫谈人机界面测试 41 9.1 一致性测试 41 9.2 信息反馈测试 42 9.3 界面简洁性测试 42 9.4 界面美观度测试 42 9.5 用户动作性测试 43 9.6 行业标准测试 43 9.7 小结 44 十 基于Web的系统测试方法 44 10.1 功能测试 45 10.1.1 链接测试 45 10.1.2 表单测试 45 10.1.3 Cookies测试 45 10.1.4 设计语言测试 45 10.1.5 数据库测试 46 10.2 性能测试 46 10.2.1 连接速度测试 46 10.2.2 负载测试 46 10.2.3 压力测试 46 10.3 可用性测试 47 10.3.1 导航测试 47 10.3.2 图形测试 47 10.3.3 内容测试 47 10.3.4 整体界面测试 47 10.4 客户端兼容性测试 48 10.4.1 平台测试 48 10.4.2 浏览器测试 48 10.5 安全性测试 48 10.6 总结 49 十一 为盈利而测试 49 11.1 引言 49 11.2 什么是软件测试 50 11.3 六个误区 50 11.3.1 误区一:忽视对正常输入的测试 50 11.3.2 误区二:忽视设计阶段的参与与评估 50 11.3.3 误区三:忽视测试计划与测试文档的建立及维护 51 11.3.4 误区四:忽视缺陷的分析,报告及跟踪 51 11.3.5 误区五:错误的测试目标及测试终止条件 51 11.3.6 误区六:不懂得合理调配使用测试人员的知识技能结构 51 11.4 软件质量与软件测试 52 11.5 软件测试的经济目的 54 11.5.1 满足用户需求,提高产品的竞争力,最终提高产品的销售量 54 11.5.2 尽早发现缺陷,降低后继质量成本 54 11.6 何时应当停止测试 56 十二 整体性能测试剖析 57 十三 性能测试工具之研究 62 13.1 性能测试的意义 62 13.2 性能测试工具综述 63 13.3 性能测试工具的体系架构 64 13.4 虚拟用户产生器 Vugen 65 13.5 Proxy 二次捕获的问题 67 13.6 关联的问题 68 13.7 脚本的问题 70 13.8 Conductor 和 Player 部分 71 13.9 Conductor 和 Player 的技术要点 72 13.10 数据分析工具 Analysis 72 13.11 结束语 72 十四 性能测试原理及性能测试实例分析 73 14.1 软件测试中的性能测试 73 14.1.1 性能测试的含义 73 14.1.2 性能测试的分解 73 14.2 一个性能测试实例 74 14.2.1 被测系统 74 14.2.2 对被测系统进行性能测试 75 14.5 总结 80 十五 软件GUI测试中的关注点 80 15.1 不能不说的二个问题 81 15.1.1 软件测试中的“二八”原则 81 15.1.2 软件黑盒测试解决的问题 81 15.2 软件黑盒测试常见错误类型及说明 81 15.2.1 用户界面错误 81 15.2.2 功能性 81 15.2.3 人机交互 82 15.3 命令结构和录入 87 15.3.1 不一致性 87 15.3.2 “最优化” 87 15.3.3 菜单 89 15.4 遗漏的命令 90 15.4.1 状态转换 90 15.4.2 危机预防 90 15.4.3 由用户进行的错误处理 91 15.4.4 其他问题 91 15.5 程序僵化 92 15.5.1 用户可调整性 92 15.5.2 控制方式 93 15.6 性能 94 15.6.1 降低程序速度 94 15.6.2 缓慢回应 94 15.6.3 如何减少用户吞吐量 94 15.6.4 反应拙劣 94 15.6.5 没有提前输入 95 15.6.6 没有给出某个操作会花很长时间的警告 95 15.6.7 程序太多提示和询问 95 15.6.8 尽量使用简单命令和提示 95 15.7 输出 95 15.7.1 不能输出某种数据 95 15.7.2 不能重定向输出 95 15.7.3 与一个后续过程不兼容的格式 96 15.7.4 必须输出的很少或很多 96 15.7.5 不能控制输出布局 96 15.7.6 荒谬的精度输出级别 96 15.7.7 不能控制表或图的标记 96 15.7.8 不能控制图形的缩放比例 96 15.8 错误处理 96 15.8.1 错误预防 96 15.8.2 错误检测 97 15.8.3 错误恢复 98 15.8.4 边界相关的错误 99 15.8.5 计算错误 100 15.9 小结 100 十六 软件测试技术 100 16.1 软件测试基础 101 16.1.1 测试目标 101 16.1.2 测试原则 101 16.1.3 可测试性 102 16.2 测试用例设计 104 16.3 白盒测试 104 16.4 基本路径测试 105 16.4.1 流图符号 105 16.4.2 环形复杂性 106 16.4.3 导出测试用例 106 16.4.4 图矩阵 108 16.5 控制结构测试 108 16.5.1 条件测试 108 16.5.2 数据流测试 110 16.5.3 循环测试 111 16.6 黑盒测试 112

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值