《前端工程化思考》:如何优化单元测试的时间?

前言:这篇文章是针对前端开发中正在使用单元测试或者使用过亦或者即将使用的同学,因为涉及到具体框架都是单元测试框架Jest,所以不了解的同学可能看的一知半解。

当然,这篇文章所体现的核心思想并不是单单针对于优化单元测试时间,而是从开发的实际情况出发,找到开发中的痛点,利用工程化的思考能力,解决实际问题。

关于如何使用单元测试,掘金已经有文章存在,我就不做过多的描述了:如何做前端单元测试

接下来我会就本人开发中的遇到的问题来思考如何优化单元测试的时间

开发中的痛点

将分支从基分支切出来进行开发完后,需要merge进目标分支,merge之前有一个CI的过程,这个过程包括lint检查、单元测试覆盖率检查等等。

随着项目体积越来越大,这个CI所需要的时间就会越来越多,每次大概花费20-30分钟,即使只修改了一个文件,而lint和单测是需要跑全量的,这就是对时间的严重浪费。

而单测又因为是一个文件一个文件的check,所以如果最后跑的那个文件单测出了问题,那么之前的时间全部就浪费掉了,又或者最后的覆盖率不通过,那么还需要重新对文件进行更改,在重跑。

以上流程对于开发人员来说就是心理上的严重折磨,开发效率严重降低。

之前所想的优化思路

之前曾经想过通过Jest提供的接口,在本地代码开发完后、push代码之前在本地强行进行单测检查,但是这样会存在以下问题:

  • 开发过程中有一个紧急bug需要修复,需要切换分支,而我已经写完的代码为了不和git stack中的存储混乱,所以不能使用git stash,只能将代码先push进远端分支,强行进行检查需要等待时间。
  • 需求尚未明确,组件开发只是先完成部分功能,为了节省时间,一般都会在组件全部完成后再写单测。而开发完一个页面组件之后将代码push到远端,又需要进行单测检查,这种情况由于单测没有写,除非忽略,否则单测肯定不通过。
  • 每次push跑单测和统一跑单测,时间会差出来不少。

所以通过Jest提供的API来完成那是不可能的了。

后续的优化思路

因为我们团队中所使用的代码仓库是github,CI所使用的action也是github所提供的action,所以就想在action中是否有一个插件,能够拿到pr中所更改的所有文件呢?

还真有!jitterbit/get-changed-files@v1这个插件就能在action中获取到所更改的文件!

拿到了所更改的文件之后,在action中run nodejs脚本,将这些文件路径通过脚本参数传入到我们所写的脚本中,就可以单独针对这些文件做单测检查。

coverge对于文件间的测试是相互隔离、相互独立的,所以若只改动了一个文件,只需要run修改的文件对应的单元测试文件就足够。而对于只修改一些配置文件,甚至可以不用跑coverage。

前端单元测试coverage主要对三个方面进行检测:逻辑、快照和覆盖率。

逻辑和快照由于单测组件的隔离性,所以是不用考虑的。而覆盖率分为两个方面:单个文件的覆盖率和整体的覆盖率。

Jest对于单个文件的覆盖率没有特别要求,而对于整体有一个百分比的限制,jest计算总体覆盖率(branch、functions、lines、statements)的方法将所有文件的覆盖行数进行求和

既然是求和,那么每次改动,只有变化的文件覆盖率有可能发生变化,所以,我们可以将本次修改的单个文件的覆盖率和上次这个文件的覆盖率作对比,就能得出整体的覆盖率的变化。对于新增的文件,可以将新增的总代码行数算到之前的总代码行数中,再将新增的覆盖代码行数算进之前的覆盖代码行数中,得出一个新增后的覆盖率,与之前作对比。

具体步骤

  1. 在master最新的分支上coverage,得出的coverage-final.json文件是覆盖率的另一种表达方式(在jest.config.json文件中coverageReporters配置项新增“json”可以得到),利用nyc这个脚本可以将其解析成total和单个文件的覆盖率json文件,拿到这个json将其push到master分支上。
    {
      "total": {
        "lines": {
          "total": 46851,
          "covered": 33931,
          "skipped": 0,
          "pct": 72.42
        },
        "statements": {
          "total": 56223,
          "covered": 40519,
          "skipped": 0,
          "pct": 72.07
        },
        "functions": {
          "total": 10412,
          "covered": 6544,
          "skipped": 0,
          "pct": 62.85
        },
        "branches": {
          "total": 26517,
          "covered": 12924,
          "skipped": 0,
          "pct": 48.74
        }
      },
      "/src/common/image.ts": {
        "lines": {
          "total": 8,
          "covered": 8,
          "skipped": 0,
          "pct": 100
        },
        "functions": {
          "total": 0,
          "covered": 0,
          "skipped": 0,
          "pct": 100
        },
        "statements": {
          "total": 8,
          "covered": 8,
          "skipped": 0,
          "pct": 100
        },
        "branches": {
          "total": 0,
          "covered": 0,
          "skipped": 0,
          "pct": 100
        }
      },
    }
    复制代码
  2. 修改项目中yml配置文件,通过jitterbit/get-changed-files@v1这个github action插件获取到此次pr修改的所有文件,并通过script参数带入到新增的脚本文件中。
  3. 在新增的脚本文件内,我们对改动的文件做筛选(没有写单测文件的、忽略单测文件的以及各种不需要单测文件的配置文件),将筛选出来的文件npm run covergae,会得到这些文件覆盖率,在通过nyc解析,拿到它们的结果。
  4. 若是修改的文件,将修改后的覆盖率和之前的作对比,若是新增的文件,将其算到总数中求覆盖率,和之前的总覆盖率进行对比。就能得到此次改动对覆盖率是增加、不变还是减少。

通过以上步骤,单元测试的时间从20min -> 3min,大大提升了效率。

这个思路是根据我的团队中开发难点进行的优化,主要思路就是将某一时刻的覆盖率进行缓存,后续的开发中通过github action插件拿到修改的文件,单独跑这些文件的覆盖率,与之前的缓存作对比,从而将其他未改动的文件所需要的单测时间化为0。

最后我想表达的是:工程化是一种能力,更是一种思想!

おすすめ

転載: juejin.im/post/7063016597971009544