关于 nodejs 覆盖率的探索

这个研究起源于这个帖子 如何收集 nodejs 服务端的测试覆盖率(Nodejs), 由于自己之前对这块其实是没有研究过的,所以特地花了些时间去了解了下。

知识储备

虽然说没有做过这块的覆盖率研究,但是对于针对nodejs这块的覆盖率工具之前是有做过一定的了解的,主要是在 nyc 以及 istanbul-middleware (以下简称IM)。

关于两者的一些说明可以去看下这两篇文章,里面讲了挺多内容的 探索istanbul/nyc代码覆盖工具的原理 React Native 代码覆盖率获取探索 (二)

nyc的初步尝试

由于nodejs这块很少会用到babel编译这种方式,所以我们就放弃了之前 聊聊前端代码覆盖率 (长文慎入) 介绍到的 babel-plugin-istanbul。 而是直接采用nyc运行前插桩的方式。

这里可以直接查看下这个栗子 nyc-expresss-coverage-demo

其实就是通过 nyc instrument的命令 对相应的代码目录进行插桩操作。 相应的插桩后的文件如:

data.js

module.exports = {
    authors: [
        {
            id: '1',
            name: 'John Irving',
            country: 'USA',
            dob: '03/02/1942'
        },
        {
            id: '2',
            name: 'Gabriel Garcia Marquez',
            country: 'Colombia',
            dob: '03/06/1927'
        },
        {
            id: '3',
            name: 'Salman Rushdie',
            country: 'India',
            dob: '06/19/1947'
        },
        {
            id: '4',
            name: 'Stanislaw Lem',
            country: 'Poland',
            dob: '09/12/1921',
            deceased: true
        }
    ]
};

变为了

function cov_106hx7e2f0() {
  var path = "/Users/sai/projects/nyc-expresss-coverage-demo/server/data.js";
  var hash = "ff103e96a45dea0c0d53a6e72b3c27f5cdf62f87";
  var global = new Function("return this")();
  var gcv = "__coverage__";
  var coverageData = {
    path: "/Users/sai/projects/nyc-expresss-coverage-demo/server/data.js",
    statementMap: {
      "0": {
        start: {
          line: 1,
          column: 0
        },
        end: {
          line: 29,
          column: 2
        }
      }
    },
    fnMap: {},
    branchMap: {},
    s: {
      "0": 0
    },
    f: {},
    b: {},
    _coverageSchema: "1a1c01bbd47fc00a2c39e90264f33305004495a9",
    hash: "ff103e96a45dea0c0d53a6e72b3c27f5cdf62f87"
  };
  var coverage = global[gcv] || (global[gcv] = {});

  if (!coverage[path] || coverage[path].hash !== hash) {
    coverage[path] = coverageData;
  }

  var actualCoverage = coverage[path];
  {
    // @ts-ignore
    cov_106hx7e2f0 = function () {
      return actualCoverage;
    };
  }
  return actualCoverage;
}

cov_106hx7e2f0();
cov_106hx7e2f0().s[0]++;
module.exports = {
  authors: [{
    id: '1',
    name: 'John Irving',
    country: 'USA',
    dob: '03/02/1942'
  }, {
    id: '2',
    name: 'Gabriel Garcia Marquez',
    country: 'Colombia',
    dob: '03/06/1927'
  }, {
    id: '3',
    name: 'Salman Rushdie',
    country: 'India',
    dob: '06/19/1947'
  }, {
    id: '4',
    name: 'Stanislaw Lem',
    country: 'Poland',
    dob: '09/12/1921',
    deceased: true
  }]
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImRhdGEuanMiXSwibmFtZXMiOlsibW9kdWxlIiwiZXhwb3J0cyIsImF1dGhvcnMiLCJpZCIsIm5hbWUiLCJjb3VudHJ5IiwiZG9iIiwiZGVjZWFzZWQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFlWTs7Ozs7Ozs7OztBQWZaQSxNQUFNLENBQUNDLE9BQVAsR0FBaUI7QUFDYkMsRUFBQUEsT0FBTyxFQUFFLENBQ0w7QUFDSUMsSUFBQUEsRUFBRSxFQUFFLEdBRFI7QUFFSUMsSUFBQUEsSUFBSSxFQUFFLGFBRlY7QUFHSUMsSUFBQUEsT0FBTyxFQUFFLEtBSGI7QUFJSUMsSUFBQUEsR0FBRyxFQUFFO0FBSlQsR0FESyxFQU9MO0FBQ0lILElBQUFBLEVBQUUsRUFBRSxHQURSO0FBRUlDLElBQUFBLElBQUksRUFBRSx3QkFGVjtBQUdJQyxJQUFBQSxPQUFPLEVBQUUsVUFIYjtBQUlJQyxJQUFBQSxHQUFHLEVBQUU7QUFKVCxHQVBLLEVBYUw7QUFDSUgsSUFBQUEsRUFBRSxFQUFFLEdBRFI7QUFFSUMsSUFBQUEsSUFBSSxFQUFFLGdCQUZWO0FBR0lDLElBQUFBLE9BQU8sRUFBRSxPQUhiO0FBSUlDLElBQUFBLEdBQUcsRUFBRTtBQUpULEdBYkssRUFtQkw7QUFDSUgsSUFBQUEsRUFBRSxFQUFFLEdBRFI7QUFFSUMsSUFBQUEsSUFBSSxFQUFFLGVBRlY7QUFHSUMsSUFBQUEsT0FBTyxFQUFFLFFBSGI7QUFJSUMsSUFBQUEsR0FBRyxFQUFFLFlBSlQ7QUFLSUMsSUFBQUEsUUFBUSxFQUFFO0FBTGQsR0FuQks7QUFESSxDQUFqQiIsInNvdXJjZXNDb250ZW50IjpbIm1vZHVsZS5leHBvcnRzID0ge1xuICAgIGF1dGhvcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgICAgaWQ6ICcxJyxcbiAgICAgICAgICAgIG5hbWU6ICdKb2huIElydmluZycsXG4gICAgICAgICAgICBjb3VudHJ5OiAnVVNBJyxcbiAgICAgICAgICAgIGRvYjogJzAzLzAyLzE5NDInXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICAgIGlkOiAnMicsXG4gICAgICAgICAgICBuYW1lOiAnR2FicmllbCBHYXJjaWEgTWFycXVleicsXG4gICAgICAgICAgICBjb3VudHJ5OiAnQ29sb21iaWEnLFxuICAgICAgICAgICAgZG9iOiAnMDMvMDYvMTkyNydcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgICAgaWQ6ICczJyxcbiAgICAgICAgICAgIG5hbWU6ICdTYWxtYW4gUnVzaGRpZScsXG4gICAgICAgICAgICBjb3VudHJ5OiAnSW5kaWEnLFxuICAgICAgICAgICAgZG9iOiAnMDYvMTkvMTk0NydcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgICAgaWQ6ICc0JyxcbiAgICAgICAgICAgIG5hbWU6ICdTdGFuaXNsYXcgTGVtJyxcbiAgICAgICAgICAgIGNvdW50cnk6ICdQb2xhbmQnLFxuICAgICAgICAgICAgZG9iOiAnMDkvMTIvMTkyMScsXG4gICAgICAgICAgICBkZWNlYXNlZDogdHJ1ZVxuICAgICAgICB9XG4gICAgXVxufTtcblxuXG4iXX0=

如果了解一定的逻辑的话就会发现其实下来node服务执行以后,就可以通过global['__coverage__'] 获取到相应的覆盖率数据。 再然后的内容就不继续说了,可以看下前面提到的知识储备中的文章,怎么去生成报告。 或者看下demo中的步骤也是可以的。

IM的运行中插桩的尝试

在上述的nyc的怎么过程中我们发现整体的过程很不方便,首先是是编译前插桩这个动作,再来就是覆盖率需要自己采集以及报告生成。

所以我们这个时候可以考虑下IM了。

IM 提供了以下的功能

  • hook了require()的方法,实际上就是被 require 的文件都会插桩。
  • 可以通过接口导出覆盖率的数据
  • 允许重置覆盖率的数据
  • 允许用户下载覆盖率的报告

这里就不举例说明怎么使用IM的了,因为他的说明文档已经很详细了,关键就是使用 im.hookLoader() 这个api就可以了。

不过这里有一个需要注意的地方, IM hook的是require()的方法, 所以这个会导致一个问题就是引入IM库的那个文件没办法被插桩, 这个是需要注意的地方(这里是个人尝试总结的,如果有哪里不对的,欢迎批评指出)。

另外在使用IM的时候还发现另外的一个问题, 那就是IM针对es6的插桩是存在问题的,类似于这个issue Updating Instrumentor (so that it supports ES6) ,其实只要更换instrument的库即可,不过作者已经不维护了,所以代码也一直未合并。

nyc运行中插桩

经过上述的一些尝试, 又重新把目光放回了nyc上,既然istanbul已经不维护,而转到了nyc上,nyc这块肯定不单单只是运行前插桩这种方式的。所以尝试又重新在网上搜索了下,结果真的有了新的发现。 nodejs 测试覆盖度工具nyc(Istanbul)简介 在这篇文章中作者执行nyc的方式并不是与mocha等测试框架结合使用的。而是直接在启动服务命令增加了nyc。

带着这个希望,重新尝试了下,发现在启动命令前 加上nyc执行以后,等到服务停止执行后,就可以在coverage 目录下生成覆盖率的报告了, 只是这里有一点不好的就是一定要服务停止以后才能够生成报告。

所以我们需要方便些的方式: 就是支持实时动态生成覆盖率报告的, 所以就有了 nodejs-coverage-lib 这个库, 其实这个库的功能还是相当的简单的,就是通过express启动了一个服务,提供了一个报告下载的接口, 而其中的过程则是获取到全局的变量__coverage__ 存储到对应的目录下后,再直接通过nyc命令生成报告,压缩最后的报告目录提供下载即可。

总结

以上就是关于nodejs这块覆盖率的一些调研以及总结。

  1. 通过nyc运行前插桩的方式进行获取到覆盖率数据,只是步骤有点小麻烦
  2. IM的运行时插桩的方式。只是可能存在es6插桩的问题以及入口文件无法获取到覆盖率的问题
  3. nyc 运行时插桩的方式目前来看是最推荐的方式了。并且通过文中的第三方库,可以省去很多过程中的步骤。

nodejs这块的覆盖率这块相对于前端来说还是会方便很多,因为主要还是覆盖率的数据就是在服务端处,免去了很多在用户端采集的过程了。

PS: 这里的覆盖率验证只是找了两个项目做了尝试,并不一定适用所有的项目(比如说typescript的项目)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值