17年年末给公司一条业务线部署了单元测试环境。这个项目比较特殊,受众是来自几十位开发者共同开发的近千个模块,有一个预装的开发环境,但是具体到每个模块,就是完全由开发者自己控制的,我们只是提供开发工具,软链到开发环境下。由于项目比较复杂,需要手动配置折腾的东西比较多,而且我们有个库-用类vue语法能同时支持h5端和微信小程序端,测试因此也要针对这种类型的模块提供h5端和小程序端的测试。一番折腾后对于部署单元测试环境感慨良多。
起步思考清楚需求
在做这一切之前,先得想清楚自己要搭的这个单测环境需要具备哪些能力。可以先从使用者的角度,譬如我的预期是:
- 一键化生成单测文件
- 单测文件中包含基础样本代码,能够测试模块和组件初始化后的data数据结构和ui结构,避免开发者面对一片空白不知从何下手,也保证最基础的能自动做的测试内容由工具来做
- 测试库mock掉开发框架级别基础库里的需要mock的内容,譬如跳转、分享等封装的功能,并且对通用测试点譬如网络请求、打点等提供封装好的mock函数。让写测试的开发者只用关心自己的代码逻辑,不因为基础库而困扰
- 开发者在当前模块下调用命令运行测试,终端输出详细的测试报告
- 测试报告的生成能力
- 监控文件变更,根据变更生成新的测试结果的能力
- 对于从vue转小程序的模块,需要一份测试代码,最终能覆盖到h5端和小程序端的测试
测试框架的能力
鉴于社区已有这么多成熟的单测框架,没必要手动开发一套单测框架,我们要做的是选套合适的单测框架,并能结合业务特点DIV到能够符合预期功能。
首先我们来看看一个完善的比较理想的单测框架需要具备哪些能力:
- 断言库: 且要和社区里常见的几种断言库如
Should.js
、chai
,语法类似,减少开发者学习成本 - 测试运行库: 能够组织、控制并运行测试,这部分可配置度越高越有利于业务化
- 测试报告:能清晰的反映测试覆盖率,能定位到为覆盖的代码。即使框架未集成,也是需要测试环境搭建者集成的
还有一些其他的方便进行测试的功能:
- 完善的mock方案:对于文件、模块、函数、对象等内容都要有可操作的mock方案
- 支持异步测试用例:毕竟异步操作会充斥在我们被测试代码的各个地方,对异步的支持是个想需求,而且需求量高,需要越方便使用越佳。
- 可调控可配置:毕竟要业务化,不能配配参数就能上路,整个测试环节的每一步我们要都能把握调度
测试流程
再来看看一个测试运行需要经过哪些步骤,来了解我们需要在哪些环节DIV
启动测试 - 测试环境初始化 - 被测试文件预处理 - 运行测试 - 获取测试结果 - 结束
-
测试环境初始化: 这是我们测试框架的代码能和开发者写的模块测试代码通信的一步,我们可以在这里传递测试代码需要的信息,譬如埋下一些全局变量。在这步也可以把测试环境需要做的准备工作做好,譬如源代码基础库的一些处理。
-
被测试文件预处理:这里是对源代码和测试代码的处理,譬如将源代码的vue代码转化为小程序代码,处理测试文件里的alias等等,需要注意的是这部比较耗时,需要注重优化,并尽量简化
-
运行测试:提供统一的接口生成组件实例,埋好各种可能需要配置的参数,测试者调用接口运行测试时,就会在你的控制下,生成实例的过程中,可以传递内置的一些信息,处理基础库的内容,挂载实例上的mock方法等等。对于需要h5转化成小程序的模块,需要先测试h5端,再转化成小程序代码,运行小程序端测试,并且封装好的测试框架要保证h5端组件实例和小程序端组件实例上挂载的方法、属性一致,让开发者能用统一的方式测试。
-
获取测试结果:拿到测试框架返回给我们的结果,视需求处理,譬如是在终端显示,还是作为pre-commit的部分阻止提交,还是在服务器上部署的失败信息回馈。
选择工具
搞清楚测试框架的能力和我们需要做的事情,接下来就是要选一款合适的测试框架和一些方便测试的库了,需要考虑以下因素:
- 可配置、可调控程度:这个非常重要,毕竟要为业务定制,而且业务不停的在发展,需要不停的加功能。可配置、可调控程度限制了你的能力,决定了你的可能性
- 被测试代码技术栈:这个跟测试框架关系不大,如果是node或者单纯的js,那么都不需要考虑太多。如果是vue技术栈或者react技术栈,就可以选择一些社区里现成的方便测试的库辅助,方便生成组件实例,以及操作组件实例,譬如vue的vue-test-utils, react的enzyme
- 常用场景:譬如我上面说的异步,如果你的源代码里异步场景比较多,那么选择一款对异步支持友好的代码绝对是开发者的福音。
测试环境
在搭建测试环境是,对自己的测试环境要有正确的认知。大多数单元测试都是部署在node端,极少有在浏览器或者微信小程序等真实生产环境下跑的。
node端的优势是一切都在掌控之中,包括测试框架的调用、测试代码和原代码的处理,不会遇到其他外来未知因素的干扰。
但是也有限制,譬如node端没有dom,即使用jsdom模拟,但因为jsdom没有css layout, dom节点位置元素丢失。
所以我们要扬长避短,单元测试毕竟注重的是最小代码单元,对于宏观环境的不利于测试的因素我们可以通过其他方式解决,譬如dom节点位置,缺失,我们可以mock调用dom阶段位置的函数返回结果来模拟,从而不影响到我们业务核心代码的测试。
个人案例分享
说了这么多,落地点到我搭建的测试环境,上面所说的期望功能都实现了,方案也在上面叙述了。每个人面对的业务场景不同,还是分享点可以共通的东西,譬如测试框架。
我选用的是jest,一方面是我个人比较熟悉,另一方面是上面所说的能力其都具备。
先说说可圈可点之处:
- jest配置完善,如果用默认配置,就是一个大全家桶,包括测试报告、检测文件变更运行测试这些能力都帮你内置了
- 虽然默认配置是全家桶,但是可配置性很高,每个单元都可拆卸替换。譬如测试运行库默认用的是
jasmine2
,但是你也可以换成其他的,一个配置即可完事 - jest发了22个大版本后,已经比较完善了,这一年看着facebook的这个儿子更新迭代的,还是很靠谱的。
- 社区繁荣,配套的react测试方案、vue测试方案资料都充足
用下来觉得不方便的地方:
- 官方文档只有命令行和配置文件的使用方式,没有任何node端调用的文档,部署环境肯定是node端,这些配置肯定是内置的,导致这些接口参数,包括暴露出的测试调用入口都是通过源码找出来的,非常不方便。
- 没有测试代码预处理器,只有原被测试代码预处理器,导致如果你需要对测试代码做些处理,都没法方便操作,只能处理后生成文件,再去读取生成后的文件,耗时很长
- 由于配置了watchman作为检测文件变更的工具,watchman不支持软链,jest内部多出逻辑也没有考虑到软链的情况,如果你的测试环境在一个项目里,被测试文件通过软链链到这个项目里,便没法操作,一定要找到原文件(这个场景比较少)
搭建单测环境,重要的还是为写测试的开发考虑,让他们用的顺手,提供的api和写法要人性化,消除业务基础库的影响,尽量封装通用的工具函数,还要有完善详细的开发文档。后期就是宣传要给力喽,能够吸引大家来写,毕竟前端领域,非框架类和基础库类的业务代码,很少有人有测试的习惯。