You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In computer programming, unit testing is a software testing method by which individual units of source code—sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures—are tested to determine whether they are fit for use _ wikipedia
通过 jest Sequencer 进行单测排序,排序规则为 failed(上次失败的先运行)> duration(耗时长的先运行) > size(文件体积大的先运行),当然这里你也可以自定义customSequencer来覆盖 jest 默认的排序规则,jest 排序规则如下。
returntests.sort((testA,testB)=>{if(failedA!==failedB){returnfailedA ? -1 : 1;}elseif(hasTimeA!=(testB.duration!=null)){// If only one of two tests has timing information, run it lastreturnhasTimeA ? 1 : -1;}elseif(testA.duration!=null&&testB.duration!=null){returntestA.duration<testB.duration ? 1 : -1;}else{returnfileSize(testA)<fileSize(testB) ? 1 : -1;}});
什么是单元测试
In computer programming, unit testing is a software testing method by which individual units of source code—sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures—are tested to determine whether they are fit for use _ wikipedia
对一个函数,模块,类进行运行结果正确性检验的工作就是单元测试,此外每个单元测试的对象应该是一个最简单的组件/函数。
那写单测又能给我们哪些收益呢?
此外,单测高覆盖率的项目也会给公司节省大量支出:
据统计结果,如上图展示,绝大多数bug都是在coding阶段产生,并且随着需求开发进度的推进,修复bug的成本会随指数级增长,当我们在unit test阶段发现并修复这个bug是能给公司带来巨大收益的。
下方介绍两种常见的项目单元测试规范:
这里驱动开发的不是简单的测试用例,是能够持续验证、重构并且对需求功能极致细化的测试用例
单元测试的书写初期必定伴随着大量精力与时间的消耗,但长期持续维护的业务在搭建并完善好整个单元测试系统后,可大大提高项目稳定性和研发效率
前端单元测试
前端单元测试框架
前端单元测试有很多框架,我们下方列出三个较为流行的框架进行介绍
测试断言库
在单测运行框架中,我们需要断言库来进行方法返回和实例状态的正确性验证
在众多前端单元测试框架中,jest 目前凭借零配置,高性能,且对于断言,快照,覆盖率等都有很好的集成,目前是较为流行的一个单测框架
jest 框架简介
这里我们简单来看下 jest 框架的特点以及大致的运行原理
jest 的整体框架特点大概归纳总结为以下几点:
打开 jest pacakges,可以看到大概有50多个包,这里我们根据这些不同的包来将整个 jest 运行流程大概串起来
第一步 jest-cli 读取相关配置
当我们执行 jest 命令时,先去执行 jest-cli 中的 run 方法,再调用 jest-core 中的 runCli 方法,其中通过 jest-config 提供的 readConfigs 来读取 jest 相关配置,返回全局配置(globalConfig)和局部配置(configs)
第二步 文件静态分析
使用 jest-haste-map 库来进行项目中所有文件的检索以及文件之间的相互依赖关系,在 jest-core 中的 _run10000 方法中执行 buildContextsAndHasteMaps,返回 contexts 和 hasteMapInstances,contexts 中的 hasteFs 就是文件以及依赖关系的存储。
�**jest-haste-map **检索的过程中借助 jest-worker 来根据当前cpu核数并行的进行文件检索,借助 fb-watch-man/crawler 对整体文件变动做实时监听,做到只执行最小改动的单元测试,实现缓存效果。
下方看一个简单的jest-haste-map使用示例
第三步 单测执行前排序
经过第一步和第二步,我们拿到了 配置对象 configs,以及文件Map HasteContext,通过 SearchSource 对象检索出所有的单元测试到一个数组中,接下来根据配置项去执行这些单测了,不过在正式执行之前,我们需要先对当前拿到的所有单测进行权重优先级排序。
通过 jest Sequencer 进行单测排序,排序规则为 failed(上次失败的先运行)> duration(耗时长的先运行) > size(文件体积大的先运行),当然这里你也可以自定义customSequencer来覆盖 jest 默认的排序规则,jest 排序规则如下。
第四步 开始执行
通过 TestScheduler 来进行单元测试执行调度,例如 scheduler 会推算是串行执行还是并行执行,scheduler 之后会调用 jest-runner 中的 runTests 方法
runTests 方法, 使用 jest-worker 创建多个worker thread 或者是 child process 池子来对 tests parallel 执行
单元测试中所写的全局方法和变量中,比如 test() describe() it() 等是由 jest-cirucs/jest-jasmine 提供并写入global中,同时真正触发执行tests的方法也在jest-circus/jest-jasmines中提供,整个单测执行流程中单测状态以及执行结果是由 jjest-circus/jest-jasmines中提供的一套类似于 redux 的数据流机制来进行管理维护
最终 js 的执行是由 jest-runtime 中提供的 vm 虚拟机隔离执行,vm 作用域中的方法,比如 setTimeout/setInterval/document等,是由 jest-environment-(node/jsdom) 提供,jest-runtime还提供了require的具体实现,mocking的具体实现,以及transformer等,在接下来的Mpx框架单元测试章节我们会去详细介绍它。
第五步 处理返回结果
当执行结果从 jest/circus 返回后,jest 对数据进行序列化处理后吐给 jest-runner和 scheduler,最后在 jest-core 中的runJest方法中进行执行结果的终端输出/文件输出等一系列处理。
小程序单元测试
与 web 应用的不同
上个章节讲完前端单测,以及jest单测框架的大概运行原理后,我们来看下小程序中的单测与web应用的不同
小程序本身是双线程分离的机制,但目前并没有这种独特的运行环境可以用来进行单元测试,这里需要借助 miniprogram-simulate 工具集,来将整体运行机制调整为单线程模拟运行,并利用 dom 环境来进行渲染,从而完成整个自定义组件树的搭建
运行小程序的单元测试依赖 js 运行环境和 dom 环境,这里我们选择 jest 框架来提供对应的环境
上边是一个简单的测试demo,具体关于miniprogram-simulate 的使用详情可以去官方文档查看 https://github.com/wechat-miniprogram/miniprogram-simulate
此外对于小程序工具集的整体运行流程,在下方章节进行了简要总结。
小程序单测框架整体流程
众所周知,小程序基础库提供组件和API,处理数据绑定、组件系统、事件系统、通信系统等一系列框架逻辑,这里miniprogram-exparser 就是小程序的组件系统模块,exparser 的组件模型和 WebComponents标准中的 ShadowDOM 高度相似,维护整个页面的节点树,包括属性,事件等,类似一个简化版的 Shadow DOM。
当然中间还有很多细节实现,比如模版渲染 j-component/template/compile,组件更新 j-component/render 等,感兴趣的话可以详细去看下里边具体的实现,这里我们暂且按下不表。至此,我们拿到了 component 实例,并可以进行正常的组件状态获取以及更新,然后在jest框架中去断言组件的各种属性以及方法执行后的预期。
Mpx 框架单元测试
经过上方 jest 框架讲解以及小程序单元测试流程分析,接下来看下在Mpx框架中的单测能力支持实现
初期版本
Mpx框架的初期单测架构,是将Mpx框架开发的小程序项目,先构建编译为源码,再使用 miniprogram-simulate + j-component + jest 对构建后的小程序原生代码运行单元测试
该方案执行任何case都需要执行完整的构建流程,而且预构建已经完成了所有的模块收集,无法使用jest提供的模块mock功能,导致业务使用成本很高,落地困难。
改良版本
通过jest提供的transform能力编写mpx-jest插件,实现在jest模块加载过程中实时地将当前的.mpx组件编译转换为原生小程序组件,再交由miniprogram-simulate加载运行测试case。该方案中模块加载完全基于jest并能实现按需编译,完美规避旧方案中存在的缺陷,缺点在于编译构建流程基于jest api重构,与mpx自身基于webpack的构建流程独立存在,带来额外维护成本,后续会将通用的编译转换逻辑抽离出来统一维护,在webpack和jest两侧复用。
首先来看下 jest-runtime 中 transform 的整体流程。
上方是整个 jest-runtime 中对于require module 时transform的整体流程。在jest的这一能力之上,我们给予 mpxjs/webpack-plugin 开发了 mpx-jest transformer,并采用 vm script 实现 require from memory 直接exec mpx-jest处理后的组件js代码。
简单的单元测试书写
在上方介绍过整体的jest框架流程以及Mpx框架单元测试架构后,接下来我们着手进行一个简单的 Mpx 框架开发的小程序组件的单元测试用例书写
使用 @mpxjs/cli 创建模版项目时选择使用单元测试,这时会生成创建好的有单测能力的模版项目,和普通 jest + miniprogram-simulate 搭建的原生小程序单测项目不同的是,transform 中添加了 mpx 文件的处理
此外我们对于 miniprogram-simulate 仓库做了一些定制化方法,比如loadMpx,mockComponent 等,这里我们 fork 了该仓库并发布了定制化包 @mpxjs/miniprogram-simulate
下方是对于 component list 的单元测试demo
示例中我们对组件的data,以及组件中生命周期触发后对应组件状态变化的预期管理,属于比较简单基础的用例
对于复杂功能组件的复杂单元测试的书写,会另起一篇文章进行介绍。
结语
通篇文章我们依次进行了前端框架简介,jest框架原理总结,小程序单元测试内部执行流程,最后介绍Mpx框架中单测能力的支持实现,学习到了jest不仅仅是一个单元测试框架,你甚至可以使用它的各个工具自己创建一个单元测试框架等,后续我们还会继续探讨推行业务中落地TDD,复杂逻辑组件单测用例规范等问题,持续在小程序单测方向深耕并有更好的规范落地。
参考文章:
https://jestjs.io
https://jestjs.io/docs/architecture
https://medium.com/code-for-cause/jest-architecture-9870bbfcda44
The text was updated successfully, but these errors were encountered: