作为一个前端,一开始并不知道单元测试的好处,觉得费时费力,效果也不明显,直到有个模块因为性能问题进行了一次重构,被折磨得筋疲力尽的时候,才发现单元测试的好处,可以说,有了单元测试,才能面对其他同事写的或者n年以前的代码,放心大胆的对其进行持续的维护甚至重构。
- 单元测试有别于集成测试,是对最小可测试单元(一般为单个函数或小组件)进行检查和验证。功能越简单越好,单个验证其他变量影响越少越好,这样能达到最好的效果。好的单元测试用例可以充当开发文档供后人阅读。
断言库 chai
单元测试的核心就是断言,通过断言来判断代码是否达到目的
node内置断言assert,以下图为最简单例子,如果错误会抛出异常
chai
这个断言库很全很强大,提供了常用的assert should expect断言关键字
expert(xxx).toBe(xxxx)
但是
egg官网推荐assert,理由是简单,不用记一些复杂api。
Mocha
Mocha是老牌的库,简单全面易用,目前公司就在用Mocha+chai测试前端数据逻辑,用来测试node,util里面的函数都可以,测试组件需要搭配其他框架。
Mocha测试的钩子 Mocha在describe块之中,提供测试用例的四个钩子:before()、after()、beforeEach()和afterEach()。 可以用before钩子提前把测试环境和数据准备好,beforeEach钩子可以在每个测试用例前准备一个或多个新的对象,防止被前面的用例污染
describe('测试index.js',()=> {
before(()=>console.info("在本区块的所有测试用例之前执行"))
after(()=>console.info("在本区块的所有测试用例之后执行"))
beforeEach(()=>console.info("在本区块的每个测试用例之前执行"))
afterEach(()=>console.info("在本区块的每个测试用例之后执行"))
describe('测试addNum函数', ()=> {
it('两数相加结果为两个数字的和', ()=> {
assert.equal(addNum(1,2),3)
})
})
})
复制代码
最后,Mocha默认每个测试用例最多执行2000毫秒,如果到时没有得到结果,就报错, 而且Mocha默认会高亮显示超过75毫秒的测试用例,可修改
sinon
sinon具有非常有特色的spies, stub, mock三个功能,有兴趣的可以尝试手写实现。
- spies的概念
spies => 间谍函数,间谍函数是Sinon最简单的部分,其它的功能都是建立在spies之上的,spies的主要用途是收集有关函数调用的信息,例如是否调用了函数等。spies的实现监听的基础上是不会影响函数本身正常调用(被监听的函数的上下文关系不会被影响)。
- stub的概念
stub完全取代了这个函数,而且拥有spies的所有功能。当使用stub时,函数将不具有原始的功能,而是替换后的函数。
- mock的概念
mock与stub的功能一样都是用来替换指定的函数,如果你想替换掉一个对象中的多个方法,这时mock就可以发挥作用了,但是如果仅仅是替换对象中的一个函数,那么stub更加简单易用,当我们使用mock的时候应该十分小心,因为大量的替换原有代码逻辑,会导致test变的脆弱。
nock或者moxios
用来模拟发送网络请求,需要替代后台接口时使用。
Karma
非jsdom,提供真实浏览器环境,由于目前各类框架已经比较完善了,现在已经基本被排除在主流框架之外
redux-mock-store
顾名思义,用于模拟 redux 中的 store。由于工作中一直用mobx,这里不多做介绍了
jasmine
使用与jest类似,以前通常和karma配合,大而全的测试框架(jest)成为主流后,用的少了
jest
jest 是facebook推出的一款测试框架,集成了Mocha,chai,jsdom,sinon等功能。所以刚入门的同学如果想应用直接可以上jest。
react官方脚手架自带jest,vue-cli3.0中也集成了单元测试框架,选择的时候会问你选mocha+chai还是jest。
- 运行命令 jest 后会自动运行项目下所有.test.js和.spec.js这种格式的文件。jest的配置默认只要在package.json中配置即可,如只测test文件夹下test.jsx文件
"jest": {
"testRegex": "/test/*.test.jsx?$"
}
复制代码
-
集成expect断言
-
异步处理 对于callback采用done()确保完成测试,对于promise自动处理,遇到reject自动抛出错误,async+await同理
-
Jest 中的 mock , 类似于sinon的 sinon,并且还可以模拟axios的网络请求
-
快照snapshot 对组件或者数据生成一份快照,每次测试自动深比较,不一致就报错
-
可以生成测试覆盖率报告,只需要在jest命令后加入 --coverage即可
jest --coverage
复制代码
enzyme && test-utils
airbnb公司推出的 enzyme ,专门用来测试react组件,相对应vue中也有test-utils包,基本是一样的功能,也是对组件挂载后进行事件触发、元素存在等判断。
这里以enzyme为例讲解,enzyme主要提供三种渲染方式:render、mount、shallow,存在以下区别:
render渲染结果是普通的html结构,shallow和mount对组件的渲染结果是react树,如果你用过chrome的react devtool插件,他的渲染结果就是react devtool tab下查看的组件结构。
shallow和mount的返回结果是个被封装的ReactWrapper,可以进行多种类似JQ的链式操作,譬如find()、parents()、children()等选择器进行元素查找;state()、props()进行数据查找,setState()、setprops()操作数据;simulate()模拟事件触发。
shallow只渲染当前组件,只对当前组件做断言,不会产生子组件;mount会渲染当前组件以及所有子组件,对所有子组件也可以做上述操作。一般交互测试都会关心到子组件,我使用的都是mount。但是mount耗时更长,内存啥的也都占用的更多,如果没必要操作和断言子组件,一般使用shallow。
总结
单元测试属于需要投入,但是看不见产出的工作,所以直接选择最简洁的框架,节约时间,才是提升kpi的关键,现在的框架越来越趋向简单和人性化,所以如果工作中使用,直接选jest类集成框架就好。
我在的toB传统软件服务公司,前端的稳定性更加重要,在一个产品快速迭代期后,单元测试也会逐步进行覆盖util模块和model层(响应action的数据变化处理),其它则需要看迭代排期,经常重构的基础公共组件也会排上。每个公司需求不同,如果是上线就撤的项目,可以不用部署测试。所以大家考虑引进单元测试前,请灵活考虑成本和收益。