babel-plugin-istanbul如何正确处理Vue文件?

211 阅读3分钟

背景

前段时间由于公司测试左移,很多需求的测试任务都落到了开发身上。但是由于组内人均需求较多,自测不充分所导致的线上问题时有发生。为了减少此类问题,我们循序渐进的在大多数项目里接入了babel-plugin-istanbul这个babel插件,然后将对应的覆盖率数据上传到内部平台进行可视化展示,从而掌握此次新需求所涉及到的代码改动是否都已自测覆盖到。

接入流程

假如你的项目是通过vue-cli创建的:

vue create vue-demo

如果初始化项目的时候遇到了如下问题:

image.png

是因为vue-cli使用了自己定义的镜像,可以通过which vue得到vue命令所在位置,然后进入bin命令同级的lib/node_modules/@vue/cli,即可找到安装到全局的vue-cli位置,然后通过vscode的code .命令打开项目:

image.png

找到这个路径对应的文件@vue/cli/lib/util/registries.js

const registries = {
  npm: 'https://registry.npmjs.org',
  yarn: 'https://registry.yarnpkg.com',
  taobao: 'https://registry.npm.taobao.org',
  pnpm: 'https://registry.npmjs.org'
}

module.exports = registries

将taobao镜像换成https://registry.npmmirror.com即可。

创建好项目后,安装babel-plugin-istanbul:

npm install --save-dev babel-plugin-istanbul

配置babel.config.js

module.exports = {
  plugins: [
    [
      'istanbul',
      {
        // 是否使用内联sourceMap
        useInlineSourceMaps: false,
        // 填入需要获取覆盖率的文件后缀,注意带'.'
        extension: ['.js', '.ts', '.vue']
      }
    ]
  ]
}

启动项目:

npm run serve

打开控制台可以通过__coverage__,得到对应的覆盖率数据:

image.png

存在的问题

运行到这儿我们就把上述__coverage__数据通过自定义的webpack plugin上传到了内部平台,上传后发现咱的数据根本就不对,我们可以在HelloWorld.vue中添加一些js代码,如:

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  mounted() {
    const num = Math.random()
    if (num > 0.5) {
      console.log('num > 0.5')
    } else {
      console.log('num < 0.5')
    }
  }
}

打开控制台,找到该文件对应的覆盖率数据:

image.png

其中89-1310分别与下图的行列号对应:

image.png

但实际上我们的源文件还有template上的代码,正确的位置应该为:4041-4442,也就是说这个数据产生了偏移:

image.png

产生如上问题的原因是:在babel-loader处理代码之前,源码经过了一系列别的loader的二次编译(如vue-loader、ts-loader等),istanbul基于编译后的代码进行插桩上报,所以行列号是基于编译后的代码而非原始代码,如:

image.png

image.png

解决方案

为了解决这个问题我们收集了loader之间的sourceMap进行逐层溯源,通过自定义babel-plugin更改ast代码,将插桩后的行列号映射到原代码的行列号,目前已经封装为单独的npm包:coverage-source-map-trace-plugin

我们在demo项目里安装:

npm install coverage-source-map-trace-plugin -D

配置好vue.config.js

const CoverageSourceMapTracePlugin = require('coverage-source-map-trace-plugin')

module.exports = {
  chainWebpack: config => {
    config
      .plugin('coverage-source-map-trace-plugin')
      .use(CoverageSourceMapTracePlugin)
  }
}

然后重新启动项目,再次检查控制台的__coverage__数据:

image.png

可以看到,此时的覆盖率数据则是准确的。此插件不仅仅针对Vue文件,只要项目是基于webpack的,配置了babel-loader处理js代码,那么所有类型的文件,经过合适loader处理之后,最终都会经过babel-loader去处理,那么就可以进行sourceMap溯源

但是有一种情况可能导致数据不正确,那就是项目不产生sourceMap,此时可以在webpack中添加如下配置:

devtool: 'source-map'

然后结合__coverage__数据中的_tracedSourceMapList这个字段,查看当前文件已经溯源的loader列表:

image.png

如果说该vue文件的script部分是ts语法的,那么上述列表里应该还有ts-loader,如果缺少了,那么就可以检查一下你的项目里是否正确配置了sourceMap

image.png

参考