代码覆盖率才是评价一个项目品质的标准。在挑选一个项目的时候,有经验的使用者都会根据代码覆盖率来确定代码的可靠性。
虽然自动化测试工具可以自动验证代码的正确性,但是如果只有部分代码经过了测试,或者只是简单地跑通了代码,也不能说是一个合格的代码。比如组件库只测试一部分的组件,或者每个组件只测试了默认设置。譬如,按钮中某个属性或者某几个属性间的排列组合并没有得到测试,这样的测试还不能保证组件库的功能完全正确。
那么如何来客观评价这个测试的完备程度呢?答案就是代码覆盖率。
JavaScript 是前端应用主要语言,相较于其他平台编程语言,JS资源多数情况下要通过网络进行加载,那么代码的体积直接影响了页面加载执行时间。“无效的代码”的多寡直接影响到了我们的代码质量,所以度量代码的执行覆盖率是一项重要的优化前置工作。
Dead code
Dead code 也叫无用代码,这个概念应是在编译时静态分析出的对执行无影响的代码,举个例子:
// a.js
const a = 1;
const b = 2; /* dead code */
export default a;
// index.js
import a from './a.js';
export default function() {
console.log(a);
}
通常我们用 Tree Shaking 在编译时移除这些 dead code以减小代码体积。
冗余代码
而代码覆盖率里所提到的冗余代码 和 Dead Code 又略有不同,简单来说Dead code适用于编译时,而 Code coverage 适用于运行时。
Dead code 是任何情况下都不会执行的代码,所以可以再编译阶段将其剔除。
冗余代码 是某些特定的业务逻辑之下并不会执行到这些代码逻辑(比如:在首屏加载时,某个前端组件完全不会加载,那么对于“首屏”这个业务逻辑用例来讲,该前端代码就是冗余的)。
覆盖率概念
代码覆盖率(Code coverage)是软件测试中的一种度量指标,描述测试过程中(运行时)被执行的源代码占全部源代码的比例。
只是简单地做到行级覆盖还远远不够,可以看看下面的例子
function getStr(a, b) {
if(a && b) {
return 'both'
}else {
return 'other'
}
}
以上代码只需要两个 Case,就可以将所有语句都跑一遍:
- Case 01: a = true ; b = true; 期待结果:‘both’
- Case 02: a =false ; b= true; 期待结果:‘other’
但是,显然这样测试甚至不能确定代码中 b 是否参与条件判断。比如下面代码可能也会出现相同的结果。
function getStr(a, b) {
if( a ) {
return 'both'
}else {
return 'other'
}
}
所以覆盖率只有简单的代码行覆盖还远远不够。
覆盖率种类
在软件工程理论中会把覆盖率分为:
- 语句覆盖(statement coverage)
- 判定覆盖(decision coverage)
- 条件覆盖(condition coverage)
- 条件判定组合覆盖(condition decision coverage)
- 路径覆盖(path coverage)
- 多条件覆盖(multi-condition coverage)
- 修正条件判定覆盖(modified condition / decision coverage)
覆盖深度的提高显然会让代码功能验证更加全面,但是相应的实施成本也会明显提高。
理论的覆盖虽然全面但是实际生产很多指标的指导价值并不是很大。在实际生产中会将它进行简化。
在 Javascript 语言中,代码覆盖率通常会分为四级覆盖。
- Function coverage 函数覆盖 - 每个函数是否被执行;
- Line coverage 行覆盖 – 每行是否被执行;
- Statement coverage 语句覆盖 - 每个语句是否被执行;
- Branch coverage 分支覆盖 - 每个 if 节点是否都被执行。
这是结合了函数式语言的特点和指标的实用程度的经验之谈,也是 JavaScript 世界比较受认可的标准。
Istanbul 代码覆盖率工具
代码覆盖率需要专用的工具生成。在 JavaScript 程序中的代码覆盖率通常都是通过 Istanbul 生成的。Istanbul 的来历是伊斯坦布尔的地毯,覆盖率报告其实就是用颜色来表示代码的运行情况,好像给代码铺上了地毯。
我们熟悉的 Jest 和组件库使用的 Vitest 测试框架,都是使用的 Istanbul。Istanbul 的实现原理是通过编译期代码插桩方式实现的。有兴趣的同学可以研究一下。咱们这里面只讲如何使用。
覆盖率原理介绍:http://www.alloyteam.com
生成覆盖率报告
在 Vitest 只需要在命令行中添加参数 --coverage 就可以生成覆盖率报告了。
文件名:package.json
"scripts": {
"coverage": "vitest run --coverage"
},
还需要安装依赖@vitest/coverage-c8
pnpm i @vitest/coverage-c8 -D
执行命令
pnpm coverage
执行完命令后会生成一个coverage目录,且控制台有相应结果
浏览器查看coverage目录的index.html
默认报告形式是一个静态网站。
如果想对报告进行进一步调整,可以在 vite.config.ts
中进行。
test: {
coverage: {
provider: "istanbul", // or 'c8',
reporter: ["text", "json", "html"],
},
},
调整的两项是:
- 指定覆盖率引擎 istanbul;
- 指定输出格式,默认是没有 json 格式的。这个和后面的覆盖率展示有关,务必要加。
安装依赖
pnpm i @vitest/coverage-istanbul -D
执行命令
pnpm coverage
公布覆盖率报告
覆盖率报告放在本地只能给自己的单元测试提供依据,更大的用途在于将覆盖率报告公布展示出来,让使用者可以看到。通常会选用覆盖率展示平台,常用的有 Codecov 和 Coveralls。
Codecov 是一个开源的测试结果展示平台,将测试结果可视化。Github 上许多开源项目都使用了 Codecov 来展示单测结果。Codecov 跟 Travis CI 一样都支持 Github 账号登录,同样会同步 Github 中的项目。
还会自动的生成徽章。这个徽章是会根据报告结果动态变化的,属于客观的第三方数据,具有公信力。
首先需要登录Codecov 官网:https://about.codecov.io/
一定要使用 Github 账号进行登录,这样它可以直接读取你的 Github 中的项目。
然后是添加访问授权,如果你的项目在你名下的组织下,也需要访问授权。
这时候你就可以在网站上找到所有 Github 中的项目。选择 【Not yet setup 】,选择需要展示覆盖率报告的网站后的【 setup repo】链接,就可以看到安装指南。
它这个安装指南是针对所有语言项目的。这里面我们只需要得到它的上传 Token 就好了。
持续集成自动更新覆盖率报告
覆盖率报告最好能够自动按照最新版本持续更新,而不是自己手动上传。所以这个时候就需要使用 CI 工具来帮忙了。首先需要编写一个 Action。
文件名:.github/wokflows/codecov.yml
# .github/workflows/main.yml
# This is a basic workflow to help you get started with Actions
name: Coverage
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
push:
branches: [master]
pull_request:
branches: [master]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
CodeCov:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2.1.0
with:
version: 7.2.1
- name: Install modules
run: pnpm install
- name: Run Test Coverage
run: cd packages/smarty-ui-vite && pnpm coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
directory: packages/smarty-ui-vite/coverage
token: ${{ secrets.CODECOV_TOKEN }}
上传报告环节同样使用现成的 Action 。这里面需要添加 Token,把上面 codecov 安装指南中得到的 Token 放入 Secrets 中。
提交代码就可以自动生成覆盖率报告了。
Codecov Action 会自动识别语言与测试框架的覆盖率报告种类,然后上传相应的数据。这里面要特别说明,对于 Vitest ,是上传的 json 格式的报告,所以需要确定你确实生成了 json 报告。如何生成可以看上文介绍。
添加覆盖率徽章
最后就是富有仪式感的一步。Codecov 网站可以将测试结果生成为徽章。你可以把徽章直接放到 Github 中展示出来。
在项目的【 Settings 】中找到对应的徽章。
覆盖率报告是项目品质的重要指标,成熟的项目都会把项目展示在显眼的位置,大家通常都会拿代码覆盖率指标评价开源代码的成熟度。当然过高的覆盖率也会带来成本的提高,和拉低项目进步速率。
品质、进度、成本是每一个项目管理中都需要均衡的东西。所以一味追求代码覆盖率也未必是好事。这个大家要客观看待。