当前位置: 首页 > news >正文

Angular进阶之九: JS code coverage是如何运作的

  • 环境准备 需要用到的包

node 18.16.0# Javascript 代码编辑"@babel/core": "^7.24.7","@babel/preset-env": "^7.24.7","babel-loader": "^9.1.3",# 打包时使用的 module, 给代码中注入新的方法# https://v4.webpack.js.org/loaders/istanbul-instrumenter-loader/"istanbul-instrumenter-loader": "^3.0.1",# 数据收集工具"nyc": "^17.0.0",# 打包工具"webpack": "^5.92.0","webpack-cli": "^5.1.4"
  • 如何使用

1. 创建一个简单的js项目,项目结构如下
├── src
│   └── index.js 
├── .nycrc
├── .babelrc
├── package.json
├── package-lock.json
└── webpack.config.js
2. 每个文件里都有什么,怎么使用
index.js
function sum (a, b) {return a + b;
}
function reduce (a, b) {return a - b;
}// 手动调用 sum 函数来生成覆盖率
sum(1, 2);
.nycrc 配置 nyc 获取哪些文件的覆盖率 数据输出 位置
{"include": ["src"],"exclude": ["dist"],"reporter": ["html", "text"],"all": true,"report-dir": "./coverage"
}
package.json
{"build": "webpack",// 使用webpack 将index.js 打包成 dist 文件"start": "node ./dist/bundle.js",// 执行打包好的文件"coverage": "npm run build && nyc npm run start",// 使用 nyc 执行 打包好的文件并抓取数据"report:html": "nyc report --reporter=html"// 使用 nyc 生成 测试报告
}

webpack.config.js

const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.js'
},
module: {rules: [// 给 webpack 添加 istanbul-instrumenter-loader module// https://v4.webpack.js.org/loaders/istanbul-instrumenter-loader/{test: /\.js$/,exclude: /node_modules|\.test\.js$/,include: path.resolve(__dirname, 'src'),enforce: 'post',use: {loader: 'istanbul-instrumenter-loader',options: { esModules: true }}}]
},
devtool: 'inline-source-map'
};
3. 生成coverage 报告

通过上面的配置一个简单的 coverage demo 已经算是完成了
执行 npm run coverage 即可在控制台看到报告的输出
从图中可以看到 index.js 覆盖率为 66.66%

如何找到没有被覆盖的代码,可以执行 npm run report:html 生成更为详细的报告,这个命令使用 nyc report --reporter=html 读取 .nyc_output 抓取的测试输出结果 json 生成html 文件
打开 coverage 文件下的 index.html 然后点到测试的那个文件即可看到详细的代码覆盖文件

  • 如何实现

https://v4.webpack.js.org/loaders/istanbul-instrumenter-loader/
code coverage 主要通过 webpack loaders istanbul-instrumenter-loader 来实现的
通过观察不使用和使用这个 loaders 的打包文件可以发现,使用 istanbul-instrumenter-loader 打包后的文件,会被注入一个非常大的 function 将当前文件里所有的function 判断 变量都做上了编号

{"path": "/Users/********/Desktop/coverage1/src/index.js","statementMap": {"0": { start: { line: 2, column: 4 }, end: { line: 2, column: 17 } },},"fnMap": {"0": { name: "sum", decl: { start: { line: 1, column: 9 }, end: { line: 1, column: 12 } }, loc: { start: { line: 1, column: 20 }, end: { line: 3, column: 1 } }, line: 1 }},"branchMap": { '0': { loc: { start: { line: 8, column: 0 }, end: { line: 10, column: 1 } }, type: 'if', locations: [{ start: { line: 8, column: 0 }, end: { line: 10, column: 1 } }, { start: { line: 8, column: 0 }, end: { line: 10, column: 1 } }], line: 8 }},"s": { '0': 0, '1': 0, '2': 0, '3': 0, '4': 0 }, "f": { '0': 0, '1': 0 }, "b": { '0': [0, 0] }
}function sum (a, b) { cov_29gy9r9jfk.f[0]++; cov_29gy9r9jfk.s[0]++; return a + b; 
} 
// statementMap: 记录定义变量的开始结束位置
// fnMap: 记录 Function 的 开始结束为止, function name
// branchMap: 记录判断的 开始结束为止
// s,f,b: 调用方法的时候调用封装的大方法然后给对应的变量 ++ 记录,调用次数
  • 编译过后的代码如何映射在原始文件中

source-map 中都有什么

{"version": 3,// 当前使用 source-map 的版本"file": "bundle.js",// 编译后的文件名"mappings": ";;;;;AAAA;AACA;AACA;AACA;AACA;AACA",// 这是最重要的内容,表示了源代码及编译后代码的关系"sources": [// 源文件名"webpack://manual/./src/index.js"],"sourcesContent": [// 转换前的的代码"function sum (a, b) {\r\n    return a + b;\r\n}\r\nsum(1, 2);\r\n\r\n\r\n"],"names": [],// 转换前的变量和属性名称"sourceRoot": ""// 所有的sources相对的根目录
}

source-map 如何对应到 源文件中的位置
这里需要用到上面的 mappings
首先 mappings 的内容其实是 Base64 VLQ 的编码表示。
内容由三部分组成,分别为:

  1. 英文,表示源码及压缩代码的位置关联
  2. 逗号,分隔一行代码中的内容。比如说 console.log(a) 就由 console log a 三部分组成,所以存在两个逗号。
  3. 分号,代表换行

所以 mappings 中的每一个字母都代表每个代码对应的位置,下面是当前 mappings 的解析结果

https://www.murzwin.com/base64vlq.html

http://www.lryc.cn/news/395667.html

相关文章:

  • el-table 鼠标移入更改悬停背景颜色
  • 【《无主之地3》风格角色渲染在Unity URP下的实现_角色渲染(第四篇) 】
  • 【linux服务器篇】-Redis-RDM远程连接redis
  • 【pytorch15】链式法则
  • C#用链表和数组分别实现堆栈
  • 【AI原理解析】—强化学习(RL)原理
  • java解析请求的字符串参数Content-Disposition: form-data;和拼接的键值对
  • 活动回顾|2024 MongoDB Developer Day圆满收官!
  • MySQL资源组的使用方法
  • python--实验7 函数(1)
  • 【力扣】数组中的第K个最大元素
  • WTM的项目中EFCore如何适配人大金仓数据库
  • 互联网3.0时代的变革者:华贝甄选大模型创新之道
  • Tomcat的安全配置
  • [笔记] 卷积 - 01 变速箱需要放置多少个加速度传感器?
  • Maya崩溃闪退常见原因及解决方案
  • 编码与梦想:我的CSDN创作5周年
  • Vue2 基础十Vuex
  • 【大模型】驾驭未知领域:LLM如何处理域外或无意义的提示
  • Docker容器 为MySQL创建新用户和授权
  • openssh9.8p1更新 修复漏洞(CVE-2024-6387)
  • 超市收银系统源码
  • word 使用手册
  • vue学习day03-指令修饰符、v-bind对于样式控制的增强、v-model应用于其他表单元素
  • JRE、JVM、JDK分别是什么。
  • 台灯护眼是真的吗?台灯怎么选对眼睛好?一文带你读懂!
  • 【学术会议征稿】第五届计算机工程与智能控制学术会议(ICCEIC 2024)
  • 【Golang】slice切片
  • 开源网安模糊测试平台SFuzz全新升级,从标准到实践助力车企安全出海
  • Go bytes包