网易支付前端精准测试实践汇总:
网易支付前端精准测试实践-总览篇
网易支付前端精准测试实践-跨文件影响范围分析
前端精准测试是为了解决漏测、范围评估遗漏的痛点,影响范围分析是前端精准测试的重要部分。
影响范围分析主要是为了分析出变动的代码又会对哪些代码会产生影响,进而得到最终的影响范围。
影响范围分析可以分为两部分,单文件范围分析和跨文件范围分析。
本次主要从跨文件影响范围分析开始说起。
跨文件影响范围分析
要分析1个文件的改动对其他文件有什么影响,主要需要考虑两个方面。
1)改动文件被哪些地方所依赖
2)改动内容是否对依赖者有影响
第1点应该没有疑问。
第2点是什么意思呢?举个例子,比如有个xx.js里面的foo方法有改动,但是引用它的地方可能只引用了foo2。这样我们认为,yy.js对xx.js有依赖关系,但本次xx.js的改动对yy.js是无影响的。
// xx.js
export const function foo() {
// 变动了xxx
}
export const function foo2() {
// 无变动
}
// yy.js
import { foo2 } from 'xx.js'
那么,接下来就要从这两方面入手。
首先是第一点,分析改动文件被哪些地方所依赖,称之为获取依赖关系。
获取依赖关系
初步的做法
调研了 webpack、rollup等构建工具的处理方式。初步形成了如下的方案。
从入口出发,将源码通过babel或acorn等解析生成ast树,并遍历ast数从 ImportDeclaration、CallExpression获取到依赖并保存,然后深度遍历所有依赖,最终获取到依赖关系图。

或者也可以站在webpack这个巨人的肩膀上,直接使用webpack,然后可以通过webpack插件获取到stats,从stats获取到依赖关系。
获取依赖关系没有问题,唯一的问题就是耗时较久。
而影响范围分析是在构建时使用的,如果因此导致构建变得很慢影响到开发、测试效率,这是不能接受的。
对此,可以采用两种方式,提前生成依赖关系并保存起来,然后在构建时直接使用。
代码提交时通过git hook拦截执行生成依赖关系并保存起来。
代码提交时通过webhook触发任务提前做分析并将依赖关系保存起来。

产生了很多不必要的分析,给服务器造成很大的压力
连贯性不好,构建时无法确定依赖关系一定生成好了
所以,更好的办法是在保持分析质量的基础上降低依赖分析的时间。
最终解决方案
最终选择了构建速度更快的esbuild。
esbuild具有以下优势:
esbuild 的底层实现是用 Go 语言编写的,Go是编译性语言,直接机器码执行,相比于Javascript少了动态解释的过程。
esbuild 使用并行处理技术,在构建过程中能够充分利用多核处理器,同时处理多个任务,从而加速整体构建过程。
esbuild 使用了一些高效的算法和数据结构,例如快速的解析器和高效的代码生成技术,以加快构建速度。
esbuild 不需要生成完整的抽象语法树(AST),而是直接在词法分析的基础上进行构建,避免了中间步骤的性能损耗。
esbuild插件主要有两个事件钩子,onResolve和onLoad。解析模块路径的时候,会触发onResolve,加载源文件会触发onLoad。在事件钩子中,可以获取到当前的文件path和它的依赖者importer。
最终方案的基本过程是:定义esbuild插件,对各种文件类型做解析并保存依赖关系,一直到完成对整个工程的扫描。
目前我们的系统中使用的前端技术栈主要是vue、typescript、jsx、less或scss。esbuild默认支持js、jsx、ts、tsx、css等文件加载。所以我们只需要对vue及less、scss做处理即可。
所以整个依赖关系获取主要包括 路径解析 和 指定vue、less、scss深入遍历的方式。
路径解析
为什么需要路径解析?我们代码中可能存在着多种导入路径。需要把他们都转成绝对路径。
// 相对路径
import Dialog from './dialog'
// 别名路径
import App from '@/views/App.vue'
// 省略了index或文件后缀。实际可能是:./common.ts 或 ./common/index.ts
import common from './common'
// 外部依赖
import Vue from 'vue'
// 样式引入带 ~
@import '~@/assets/style/mixin.less';
// monorepo软链接引用
import utils from '@epay/shared/utils'
这些自己处理的话还是挺麻烦的。最终使用了 webpack开源的enhanced-resolve来处理路径。
build.onResolve({ filter: /.*/ }, async (args) => {
let { importer, path: id } = args
/* 前置过滤一些不需要关心的文件类型 */
// 获取解析后的绝对路径
const resolvedId = await resolve(id, path.dirname(importer))
/* 过滤一些不需要关注的文件,比如单测文件、mock等 */
// 依赖收集
graph.addModule(resolvedId, {
importer: cleanUrl(importer),
rawRequest: args.path
})
/* 其他... */
}
vue、less等文件深入处理
对于esbuild默认不支持的文件类型,esbuild会不知道如何处理它,执行到就会报错。

vue文件
解析出script代码块和style代码块,然后就转化为了处理script和style。
至于解析出script和style的方式,可以使用vite预构建的处理方式。
也可以使用 @vue/compiler-sfc 解析出ast来处理。
less和scss文件
通过postcss-scss和post-less将转化为ast,然后遍历import即可获取到依赖。
然后可以转化为import的方式交由esbuild继续深入处理。
{
// 把样式文件当做js的导入,用于再次对这些依赖的样式做进一步深度的分析
loader: 'js',
contents: resolvedIds.map((request) => {
return `import ${JSON.stringify(request)}`
}).join('\n') + '\nexport default {}',
pluginData: {
importer: path
}
}
全局组件和全局变量
上面的依赖关系都是通过分析ast来获取的,但是全局组件、全局变量(比如vue项目中挂载在this上的方法模块)使用的地方,并没有显式依赖,在ast树上是获取不到的。
这里只对全局组件作出说明。全局变量实现方式类似。
识别出全局组件
有哪些全局组件以及使用了哪些全局组件。
有哪些全局组件
由于各个系统实现上的差异,精准测试程序本身无法很好的获取。那么我们暴露出配置项,通过配置传入即可。应用层也不用手动维护,一般我们的全局组件都是定义在统一的目录,然后通过Vue.component注册的,通过脚本来统一获取即可。
使用了哪些全局组件
其实就是判断一个文件有没有使用全局组件
通过对Vue的template在解析为ast,并遍历所有的VElement,并把htmlTag和svgTag给过滤掉(@vue/shared提供了方法,不用再单独列举了),然后把本文件注册的组件过滤掉。即可初步得到使用的全局组件。
另外,还可能有 web-component。这个我们系统中目前没有使用,如有可通过配置指定。
另外,还会再判断在配置的全局组件中,才会最终确定文件使用的全局组件。
然后把当前文件和引用的全局组件加入依赖关系。
const { tagNames } = parseVueTemplate.parseAstForVue()
// 判断 tagNames是否是全局的。如果是全局的,添加依赖
if (tagNames.size > 0 && globalComponents) {
tagNames.forEach((_, key) => {
if (globalComponents[key]) {
graph.addModule(globalComponents[key], {
importer: filePath
})
}
})
}
通过以上方式,获取依赖关系就完成了。
统计了近期执行精准测试生成依赖关系耗时情况,最复杂的系统耗时5秒内,其他系统一般是1-2s,符合预期。
影响性分析
这部分是为了分析原代码变动diff有没有对其他文件有影响,有影响的话得到最终影响到的结果。
我们考虑的不止js,还可能有js对template的影响、css对template的影响。
这其实是个比较复杂的过程,因为可能存在多个依赖或循环依赖。
例如,如下的依赖关系。
如果A有变动,需要分析A的变更是否影响到B,如果影响到B,又要判断B的影响是否影响到C,如果影响到C,继而还需要判断C的影响是否影响了A。很容易就陷入循环。

每次分析之后把结果保存下来,下次再需要分析时判断是否有分析结果,如果有则使用增量diff进行分析;如果无,则第一次做全量diff的分析。
影响性分析整体流程如下:

变动文件为vue文件
直接认为有影响,把导入行标记变动。
如果是全局组件,那么没有导入行,把全局组件使用的地方标记为变动。
变动文件为js/ts文件
分析变动影响,比较变动的exports是否被imports。如是则将导入行标记变动。
变动文件为less/scss/css等样式文件
目前只对Vue有影响,分析对Vue模板的影响,如有影响则影响行标记变动。
总结
通过以上方式,我们就实现了跨文件影响范围分析,并且具有连贯的流程、较短的处理时间和精准的处理效果。
-- End --
点击下方的公众号入口,关注「技术对话」微信公众号,可查看历史文章,投稿请在公众号后台回复:投稿




