天天看点

React前端功能测试覆盖率统计-插桩

1.React工程插桩教程

对于前端的覆盖率统计,单元测试的可以i通过jest和Enzyme配合进行单元测试并统计覆盖率,但是现在说的覆盖率主要是指功能测试的覆盖率。

而对于功能测试的测试覆盖率,肯定是需要进行代码插桩然后统计代码执行的密度,进行覆盖率统计,而插桩的库主流的就是istanbul

一,对于node项目

可以使用istanbul-middleware来进行插桩以及覆盖率统计,可以参考样例https://github.com/chenhengjie123/middleware-browser-coverage-demo

在依赖中添加istanbul-middleware和nopt,

然后使用istanbulMiddleware.hookLoader(__dirname , {verbose:true})来注入钩子返回插桩代码

app.use('/coverage', coverage.createHandler({ verbose: true, resetOnGet: true }));

app.use(coverage.createClientHandler(publicDir, { matcher: matcher }));

暴露/coverage来获取覆盖率,然后可以通过远程请求获取覆盖率或这定时i上报采集分析覆盖率,覆盖率信息保存至全局变量window.__coverage__中

二,对于React项目

因为React是属于客户端,react的代码,都会通过webpack打包编译成纯的js文件然后放到服务端当作静态文件执行,我们本地的环境实际

是通过React和node模拟了一个服务端,那么如果要进行覆盖率统计需要对react组件进行插桩然后编译成js执行。

插桩有两种方式,一种是nyc(istanbul的命令行接口),还有一种是使用babel插件

1.使用nyc方式,nyc instrument [input] [output]

nyc instrument src ..\monitortool\jddl-app-client-back\src

如果插桩失败的可以使用npm install -g [email protected],这个问题i实际是高版本的nyc做出了一些限制,修改下配置也是可以的

新建一个工程作为采集服务:wind-common-middleware(采集服务)

将需要插桩的代码考到该工程中

 cp -r wind-common/src wind-common-middleware

 然后进入该工程

 cd wind-common-middleware

 进行插桩

 nyc instrument src ../wind-common/src

 启动wind-common

 启动wind-common-middleware

 问题:这种方式插桩后的代码编译失败,主要的问题有:

 ①.引用问题,引用必须在第一行,插桩后的引用不在第一行

 ②.引用代码被修改 

 import {Layout,Menu} from 'antd';被改成了import{Layout,Menu}from'antd';导致引用失效

 ③.某些代码查妆后不能执行

 const{SubMenu}=Menu;   ======>    const{SubMenu}=(cov_28x5j2q2at.s[0]++,Menu);

 2.使用插件方式

因为上面第一种方式的问题,那么我们可不可以插桩后再通过babel编译运行呢,

结果是不行的,代码似乎没有问题,没有了import也没有其他的语法问题,但是依然还是过不了react这一关,还是会报错,依然是之前的那些问题,那么这样只有一条路可以走了,是用babel编译后的代码进行插桩,使用插件babel-plugin-istanbul

 首先项目的目录结构如下

React前端功能测试覆盖率统计-插桩

引入依赖:

    "@babel/cli": "7.7.7",

    "babel-loader": "8.1.0",

    "babel-plugin-istanbul": "6.0.0",

    "@babel/preset-react": "7.8.0"

babel-cli是babel编译的主要依赖

babel-loader是文件处理器,在webpack的配置文件中需要配置react组件使用此loader来进行传输处理

babel-plugin-istanbul是插桩的主要依赖

babel-preset-react是react使用babel必须的依赖

如果需要在不同的环境启用istanbul插件则还需要引入"@babel/preset-env": "7.7.7",

新建.babelrc文件,内容

{

    "presets": ["@babel/react"],

    "plugins": ["istanbul"]

  }

webpack.config.js文件如下

const path = require('path');

module.exports = {

  mode: 'development',

  entry: './src/index.js',

  module: {

    rules: [

      {

        test: /\.(js|jsx)$/,

        exclude: /node_modules/,

        use: [{

          loader: 'babel-loader'

        }]

      }

    ]

  }

};

package.json中的脚本文件如下

"scripts": {

    "start": "react-app-rewired start",

    "build": "react-app-rewired build",

    "test": "react-app-rewired test"

  },

然后完成后,启动报错

React前端功能测试覆盖率统计-插桩

按照要求 npm install -save @babel/plugin-proposal-class-properties

依然报错:

React前端功能测试覆盖率统计-插桩

添加依赖 npm install -save @babel/plugin-transform-modules-commonjs

在添加一个增强性能的 @babel/plugin-transform-modules-commonjs

启动后依然有问题,虽然没有报错,但是关于antd的css样式全没了

React前端功能测试覆盖率统计-插桩

最后呢,通过文章https://testerhome.com/topics/24122了解到:

是因为istanbul和babel-plugin-import冲突,babel-plugin-import是一个antd ui库的按需加载的插件, 因为antd的使用非常的广泛, 基本上我们的前端项目都会使用到这个ui库, 所以注定这个问题会遇到了

相关的问题在istanbul issue中也可以找到 https://github.com/istanbuljs/babel-plugin-istanbul/issues/161 文中提到的解决方案有两种:

1.直接修改babel-plugin-import的源码。

2.修改自己引用ui库的方式。

上述两种都比较麻烦,然而我们在机缘巧合下发现 可以通过在babelrc中引入 @babel/plugin-transform-modules-commonjs 也可以解决这个问题。不过原因暂时还不清楚(前端的打包真的太深奥了)

我们引入了@babel/plugin-transform-modules-commonjs后,发现依然有样式问题,那么可以分析得到肯定是babel-plugin-import没有生效,最后发现是因为自己没有在.babelrc中进行配置,配置后的.babelrc文件完整如下:

{

    "presets": ["@babel/react"],

    "plugins": ["@babel/plugin-proposal-class-properties","@babel/plugin-transform-modules-commonjs",

      "istanbul",["import",{"libraryName":"antd","style":"css"}]]

  }

到此,配置全部完成

最终的package新增依赖:

    "@babel/core": "^7.12.3",

    "@babel/plugin-proposal-class-properties": "^7.12.1",

    "@babel/plugin-transform-modules-commonjs": "^7.12.1",

    "@babel/plugin-transform-runtime": "^7.12.1",

    "@babel/cli": "7.7.7",

    "babel-loader": "8.1.0",

    "babel-plugin-istanbul": "6.0.0",

    "@babel/preset-env": "7.7.7",

    "@babel/preset-react": "7.8.0"

.babelrc配置:

{

    "presets": ["@babel/react"],

    "plugins": ["@babel/plugin-proposal-class-properties","@babel/plugin-transform-modules-commonjs",

      "istanbul",["import",{"libraryName":"antd","style":"css"}]]

  }

webpack配置:

config.module.rules.push({

            test: /\.(js|jsx)$/,

            exclude: /node_modules/,

            use: [{

              loader: 'babel-loader'

            }]

          });

启动成功后,可以看到能够获取到window.__coverage__

React前端功能测试覆盖率统计-插桩

看source可以看到是查妆后的代码:

React前端功能测试覆盖率统计-插桩

完成

遇到的问题:

1.添加依赖修改配置后,前端插桩成功,但是编译失败,报错,讲webpack配置

config.module.rules.push({

            test: /\.(js|jsx)$/,

            exclude: /node_modules/,

            use: [{

              loader: 'babel-loader'

            }]

          });

改为

config.module.rules.unshift({

            test: /\.(js|jsx)$/,

            exclude: /node_modules/,

            use: [{

              loader: 'babel-loader'

            }]

          });

也就是将babel-loader排在第一位,问题解决,可能原因是依赖中有添加loader的,导致插桩后代码出错

2.关于babel的依赖问题,在babel6和babel7中,版本存在很大的区别

所以在babel6中,我们需要依赖6的一些包:

"babel-core": "6.26.3",

 "babel-plugin-import": "1.11.0",

"babel-preset-env": "1.7.0",

    "babel-preset-react": "6.24.1",

    "babel-cli": "6.26.0",

    "babel-loader": "6.4.0",

    "babel-plugin-istanbul": "5.0.0"

babel7移除了stage,所以在babel7中完全就不需要stage了

推荐几篇相关的文章:

React Native 代码覆盖率获取探索 (二):https://testerhome.com/topics/8919

前端精准测试探索:覆盖率实时统计工具:https://www.infoq.cn/article/UPfOuyQIkVr6708DNV4V

聊聊前端代码覆盖率 (长文慎入):https://testerhome.com/topics/24122

Vue 应用的代码覆盖率:https://my.oschina.net/u/4592325/blog/4554268