Skip to content

14.品质保证:覆盖率测试报告

1841字约6分钟

2024-10-06

代码覆盖率才是评价一个项目品质的标准。在挑选一个项目的时候,有经验的使用者都会根据代码覆盖率来确定代码的可靠性。

虽然自动化测试工具可以自动验证代码的正确性,但是如果只有部分代码经过了测试,或者只是简单地跑通了代码,也不能说是一个合格的代码。比如组件库只测试一部分的组件,或者每个组件只测试了默认设置。譬如,按钮中某个属性或者某几个属性间的排列组合并没有得到测试,这样的测试还不能保证组件库的功能完全正确。

那么如何来客观评价这个测试的完备程度呢?答案就是代码覆盖率。

覆盖率概念

代码覆盖率(Code coverage)是软件测试中的一种度量指标,描述测试过程中(运行时)被执行的源代码占全部源代码的比例。

覆盖率指标种类

只是简单地做到行级覆盖还远远不够,可以看看下面的例子。

function getName(a, b) {
  if (a && b) {
    return "Jane";
  } else {
    return "Tony";
  }
}

以上代码只需要两个 Case,就可以将所有语句都跑一遍:

  • Case01: a = true ; b = true; 期待:Jane
  • Case02: a =false ; b= true; 期待:Tony

但是,显然这样测试甚至不能确定代码中 b 是否参与条件判断。比如下面代码可能也会出现相同的结果。

function getName(a, b) {
  if (a) {
    return "Jane";
  } else {
    return "Tony";
  }
}

所以假设覆盖率只有简单的代码行覆盖还远远不够。

在软件工程理论中会把覆盖率分为:

  • 语句覆盖(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 世界比较受认可的标准。

用户故事 (UserStory)

为组件库发布覆盖率报告,使测试覆盖率可视化。

任务分解 (Task)

  • 生成覆盖率报告;
  • 将覆盖率报告任务加入到 CI;
  • 上传覆盖率展示平台 Codecov;
  • 获取覆盖率徽章。

Istanbul 代码覆盖率工具

代码覆盖率需要专用的工具生成。在 JavaScript 程序中的代码覆盖率通常都是通过 Istanbul 生成的。Istanbul 的来历是伊斯坦布尔的地毯,覆盖率报告其实就是用颜色来表示代码的运行情况,好像给代码铺上了地毯。

我们熟悉的 Jest 和组件库使用的 Vitest 测试框架,都是使用的 Istanbul。Istanbul 的实现原理是通过编译期代码插桩方式实现的。

http://www.alloyteam.com/2019/07/13481/

生成覆盖率报告

在 Vitest 只需要在命令行中添加参数 --coverage 就可以生成覆盖率报告了。

需安装 v8 或 istanbul

# For v8
pnpm i -D @vitest/coverage-v8

# For istanbul
pnpm i -D @vitest/coverage-istanbul

要对其进行配置,需要在配置文件中设置 test.coverage 选项:

// vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    coverage: {
      provider: "istanbul", // or 'v8',
      reporter: ["text", "json", "html"],
      // include: ['src/**/*.ts', 'src/**/*.tsx'], // 指定你要测试的文件路径
      // exclude: ['src/*.ts', 'src/utils/**'],// 指定你要忽略的文件路径
    },
  },
});

添加脚本

"scripts": {
    "test:coverage": "vitest run --coverage"
},

运行结果

查看上面的 index.html

默认报告形式是一个静态网站。

公布覆盖率报告

覆盖率报告放在本地只能给自己的单元测试提供依据,更大的用途在于将覆盖率报告公布展示出来,让使用者可以看到。通常会选用覆盖率展示平台,常用的有 Codecov 和 Coveralls。

Codecov 是一个开源的测试结果展示平台,将测试结果可视化。Github 上许多开源项目都使用了 Codecov 来展示单测结果。Codecov 跟 Travis CI 一样都支持 Github 账号登录,同样会同步 Github 中的项目。

还会自动的生成徽章。这个徽章是会根据报告结果动态变化的,属于客观的第三方数据,具有公信力。

首先需要登录 Codecov 网站一定要使用 Github 账号进行登录,这样它可以直接读取你的 Github 中的项目。

然后是添加访问授权,如果你的项目在你名下的组织下,也需要访问授权。

这时候你就可以在网站上找到所有 Github 中的项目。选择 【Not yet setup 】,选择需要展示覆盖率报告的网站后的【 setup repo】链接,就可以看到安装指南。

这里面我们只需要得到它的上传 Token 就好了。 获取 CODECOV_TOKEN

上传报告环节同样使用现成的 Action 。这里面需要添加 Token,把上面 codecov 安装指南中得到的 Token 放入 Secrets 中。

持续集成自动更新覆盖率报告

覆盖率报告最好能够自动按照最新版本持续更新,而不是自己手动上传。所以这个时候就需要使用 CI 工具来帮忙了。首先需要编写一个 Action。

.github/wokflows/codecov.yml

name: Run tests and upload coverage

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    name: Run tests and collect coverage
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: pnpm/action-setup@v3
        with:
          version: 9.8.0
      - name: Install modules
        run: pnpm install

      - name: Run tests
        run: npx vitest run --coverage

      - name: Upload results to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

提交代码就可以自动生成覆盖率报告了。

提示

git add . 前先把 coverage/添加到 .gitignore 文件,这个没必要上传

Codecov Action 会自动识别语言与测试框架的覆盖率报告种类,然后上传相应的数据。这里面要特别说明,对于 Vitest ,是上传的 JSON 格式的报告,所以需要确定你确实生成了 JSON 报告。

图中底部还有对应的链接,点击去查看测试覆盖率,也可以登录 Codecov 查看更详细的说明。

添加覆盖率徽章

最后就是富有仪式感的一步。Codecov 网站可以将测试结果生成为徽章。你可以把徽章直接放到 Github 中展示出来。

在项目的【 Configuration 】中找到对应的徽章。

提示

更新:codecov 上现在已经把 Settings更名为 Configuration

添加到 README 中后推送到 Github 即可看到效果。