测试覆盖率 ReactNative 多端代码覆盖率调研及实践

zailushang · 2020年05月20日 · 最后由 Liy 回复于 2020年11月06日 · 157 次阅读
本帖已被设为精华帖!
特别感谢 @chenhengjie123 @Lihuazhang @zsx10110 三位整个过程中的帮助。

1、背景

一些新项目使用 React Native 编写,为了多维度把控产品质量,特增加代码覆盖率统计纬度;
参考资料:

2、方案介绍

  • 插桩方案:nyc 是新版 istanbul,解决了大量 istanbul 存在的问题;
  • 本地统计:nyc 和 istanbul 都用于【本地运行,本地统计覆盖率】的场景。
  • 多端统计:istanbul-middleware 是 istanbul 的一个 用于【多端运行,在服务端统计覆盖率总和】的场景 所以,我们采用 nyc 进行插桩,利用 istanbul-middleware 进行多端统计;

3、示例及代码说明

3.1 js 覆盖率报告示例

首先可以从图中看到,nyc 提供的 js 代码覆盖率报告是从 Branches(分支)、Functions(函数)、Lines(行) 三个维度统计的。

3.2 代码示例

RN 代码覆盖率 示例代码见:https://github.com/OnTheWay111/RNCodeCoverageExample
建议:先下载代码,然后看下文介绍,来熟悉整个代码结构。
如果要看原理:请看横捷之前的文章,写得非常好,https://testerhome.com/topics/8919

3.3 代码配置过程

(1)代码结构介绍

  • index.js: 项目入口文件
  • package.json:项目依赖管理文件

(2)JS 插桩步骤

cd coverage_middleware  # 进入coverage_middleware项目目录
mv ../RNdemo/js RNdemo  # 将RN项目RNdemo的js源码移动到coverage_middleware目录下
npm install  # 安装项目依赖包
  • RN 的 js 文件插入定时回传覆盖率数据的方法(每隔 4 秒自动回传,fetch 方法是 react native 提供的网络请求方法):

// post window.__coverage__ to server every 2 seconds
setInterval(function() {
  fetch('http://10.60.139.6:9999/coverage/client', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(window.__coverage__)
  })
  .then(function() {
    var date = new Date();
    console.log(date + "发送覆盖率成功!");
    console.log(window.__coverage__);
  })
  .catch(function(error) {
      console.log('发送覆盖率失败: ' + error.message);
      });
}, 4000);
  • 执行插桩 shell nyc instrument RNdemo ../RNdemo/js # 将源码插桩后,放到../RNdemo/js

至此,插桩完成。

(3)启动服务端项目

npm index

启动成功如图所示:

(4)安装 RNdemo 到手机

  • 确保手机连接电脑成功
  • 进入 RNdemo 目录, 运行 npm install
  • 运行 yarn android 等待安装(这里有疑问的,看 ReactNNative 中文官方文档:https://reactnative.cn/docs/getting-started.html
  • 安装成功后,如图:

左图为手机,有图为 reactNative 控制台,图中可以看到打印的覆盖率回传 log,以及每次回传的覆盖率对象;

4、注意

因发现 istanbul-middleware 项目已经几年没有更新代码,且没有合并 pull request,所以在自己的 git 把之前的 pull request 均合并了一下,所以建议 package.json 中配置更新后的 istanbul-middleware。

5、踩坑记录

(1)【done】react-native 启动时红屏报错:Unable to load script.Make sure you're either running a metro server or that ....
解决方法:https://www.cnblogs.com/shizk/p/11189978.html

(2)【done】nyc 插桩无效

  • 问题原因:nyc 需要 14.1.1 版本,结果在 package.json 配置 npm install 后,直接运行命令 nyc,用的是全局 nyc,并不是项目依赖包 node_modules 中的 nyc。
  • 建议:在 package.json 中配置 nyc 的 script,配置后,nyc 使用的就是 node_module 下的 nyc,而非全局 nyc。配置如下图:

(3)插桩后丢文件
nyc instrument 后发现有丢文件的现象,所以为了安全起见,还是加上完成 copy 的参数吧;

nyc instrument --complete-copy [input] [output]

(4)含有装饰器的 js 无法插桩
目前还未找到解决方法

(5)【待确认】istanbul-middleware 覆盖率数据不准确
网友发现的问题,目前还没复现,回头等复杂项目验证后再出结论。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
最佳回复

受这篇帖子的启发,我也对公司的 WEEX 项目代码做了覆盖率统计。 现在已经大体完成了。 这里补充一个踩过的坑,可能 RN 没有,WEEX 有。 Express 框架在调试的时候,会报一个错

Access to fetch at 'http://10.96.2.20:8889/coverage/client' from origin 'http://10.96.4.42:8092' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

具体错误的意思大家可以自行搜索一下。 补上修复后的代码。

var express = require('express'),
    im = require('istanbul-middleware'),
    isCoverageEnabled = true,
    app = express(),
    port = 8889;
var cors = require('cors')

app.use(cors())
// add the coverage handler
console.log('Coverage reporting at /coverage');
app.use('/coverage', im.createHandler({ verbose: true, resetOnGet: true }));

console.log('Starting server at: http://localhost:' + port);
app.listen(port);
共收到 21 条回复 时间 点赞
恒温 将本帖设为了精华贴 05月20日 13:28

666,不过你们考虑把 RN 跟其他客户端整合成一套平台吗,像阿里今年那个实时染色一样?

williamfzc 回复

覆盖率这块不会做那么多工作,目前主要就是获取多端多方式的覆盖率数据和报告。

我们没用过 RN,不过有踩坑记录的帖子都是实打实的战斗贴👍

simple 回复

全靠横捷之前的帖子,才能 3 天搞定,不然估计半个月才行

又增加了两个坑:

请教一下,插桩后文件会变很大,比如一个 7MB 的 js 文件 插桩后变成 70 多 MB,有遇到过这种问题吗?

chenabc1 回复

你的问题我没碰到过 我看了一下我的文件,插桩前 2KB,插桩后 6KB。

zailushang 回复

是用 webpack 编译后的文件进行插桩的吗?

chenabc1 回复

不是,直接用 nyc 对 js 的源文件插桩的

15.0.1 的 nyc 默认是不启用 jsx 的解析器的,所以会比 14.1.1 的版本插桩范围小,手动打开即可

受这篇帖子的启发,我也对公司的 WEEX 项目代码做了覆盖率统计。 现在已经大体完成了。 这里补充一个踩过的坑,可能 RN 没有,WEEX 有。 Express 框架在调试的时候,会报一个错

Access to fetch at 'http://10.96.2.20:8889/coverage/client' from origin 'http://10.96.4.42:8092' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

具体错误的意思大家可以自行搜索一下。 补上修复后的代码。

var express = require('express'),
    im = require('istanbul-middleware'),
    isCoverageEnabled = true,
    app = express(),
    port = 8889;
var cors = require('cors')

app.use(cors())
// add the coverage handler
console.log('Coverage reporting at /coverage');
app.use('/coverage', im.createHandler({ verbose: true, resetOnGet: true }));

console.log('Starting server at: http://localhost:' + port);
app.listen(port);
yinwenchang 回复

要怎么解决呢?有方法吗? 不过我看文档的说明是说 15 的版本是默认开启 jsx 的解析的,之前 14 的版本是不开启的,感觉貌似有点相反了。

saii 回复

nyc instrument 有个 parser-plugins 参数,命令行应该可以用,但我没试过,我是在 package.json 文件中加了配置

"nyc": {
   "parser-plugins": [
     "jsx",
     "asyncGenerators",
     "bigInt",
     "classProperties",
     "classPrivateProperties",
     "dynamicImport",
     "importMeta",
     "objectRestSpread",
     "optionalCatchBinding"
   ]
 },

其中 jsx 为我添加的,其它都是它的默认值,我通过 nyc instrument --help 查了填上去的,我不知道去了会不会有太大影响

yinwenchang 回复

嗯 验证了下 确实是这样子,感谢呀。

zailushang 回复

看下 16 楼的回复,可以不回滚版本也是能解决那个问题的。

saii 回复

哈哈,可以

20楼 已删除
22楼 已删除

您好,遇到了同样的问题,使用装饰器的代码无法插桩,请问现在有解决方法了吗?

Liy 回复

目前我没做这个了,可以问问这位老哥 @saii

zailushang 回复

好的,谢谢

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册