前言:
每次看文档,首先关注的不是内容,伴随着中文文档这几个字映入眼帘,心里肯定是有一块石头落地的,所以当我看到jest这个封面的时候,一股选对了框架的自豪感油然而生。
结果点进去一看,也只有标签跟快速入门这一页是中文了???
联想到高中时候上课,数理化老师冲进来,理直气壮地说:“学好数理化,走遍天下都不怕。”
生物老师紧跟着步伐,大声叫嚣着,“没学好生物,以后都不知道小孩是不是你的!”
随后赶到的是刚刚在几分钟前无意间看见了jest文档的英语老师,似乎也找到机会扬眉吐气了,只听他清了清嗓子,数理化生没学好,“你至少还能去当个前端,要是连英语都学不好,哼哼,我看你们以后还能去干什么。”
班主任听完,立刻显出颓唐不安模样,脸上笼上了一层灰色,嘴里说些话,这回可是全是之乎者也之类,一些不懂了……
开始
前几天写了一篇stencil编写web-component,本来是准备直接写几个组件发布到npm的,但是本着认真负责的态度,和万一除了自己还有其他人用的这种不切实际的想法,单元测试确实是必不可少的。选择jest也是因为stencil文档用的就是它。虽然它的中文文档挂羊头卖狗肉,但是瑕不掩瑜,该有的功能都有,该支持的框架也都支持。
起步
首先肯定是npm install jest -g,全局安装下jest,然后在目录下npm init初始化一下,把package.json中的test,改成“jest”,最基本的配置就算是完成了。
接下来先编写一个简单的例子,创建一个sum.js,求两数之和,接着,创建一个sum.test.js
用来测试刚刚编写的sum
从语义上就挺好理解的,期望sum(1+2)的结果是3 。
例子写完,直接运行npm run test就可以在控制台看到输出结果:
如果需要看到更详细的测试信息,比如代码覆盖率,可以在控制台运行jest --init,就会出现一些配置选项让我们选择。
看着选就完事了,选完之后目录里就会出现jest.config.js,点进去就能看到各种配置了,在第二十行,把collectCoverage设为true
然后再重新运行npm run test
会发现输出的结果上多了一个表格,表格主要是体现我们的单元测试对代码的覆盖率,
- %stmts是语句覆盖率(statement coverage):是不是每个语句都执行了
- %Branch分支覆盖率(branch coverage):是不是每个if代码块都执行了
- %Funcs函数覆盖率(function coverage):是不是每个函数都调用了
- %Lines行覆盖率(line coverage):是不是每一行都执行了 至于jest.config.js剩下的配置都可以对照着上面的英文注释来决定要不要开启。
主要内容 因为jest的api比较多,但可能我们一般常用的也就那么几个,就简单地举几个例子来说明一下。对于我来说一般的需要测试的内容可以分为以下几个类:
- 对功能function的测试(传入参数,返回一个值,上文的sum函数,不多做赘述)
- 回调函数(callback)或异步读取数据后对数据的处理过程的测试
- 对整个类(class)的测试
- 对stencil编写的web-component组件的测试
- 框架内的组件测试,如vue,react
回调函数(callback)或对异步数据的处理
1.回调函数
很简单的一个例子,不过正确的结果是123,这里写成了234,来看看expect结果不一致我们能看到什么。
控制台直接会输入失败的单元测试,并告诉我们错在哪,然后把234改回123,输出的结果自然也就都通过了。
2.带定时器的回调函数
先写个小例子,直接运行npm run test
会发现虽然通过了,但是覆盖率却只有60%,而且3-4行的代码并没有运行到,对比到setTimeout.js中的代码,刚好是回调函数中的两句没有执行。
加个assertions再执行就能发现,我们回调函数中的except并没有被运行,jest并没有乖乖等到timeout结束去执行cb。
对此,官方提供了2种方法:
使用done参数
test的回调函数中可以提供一个参数done,只有当done运行时,整个test函数才算结束
使用jest内置的timers
Jes还提供了我们一个fakeTimer的api,能够让我们用来模拟定时器。能够使用api来改变执行的进行时间。
3.异步数据处理(promise,http)
在单元测试时,我们可能不需要真实地从后台去获取数据,而是可以用过mock数据来进行测试,然后用mock的数据来进行操作。于是原来的api模块可能就无法使用了,jest也为我们提供了模拟模块的功能,在对应的需要引用的模块的相同目录下的,创建一个__mock__的文件夹,然后命名一个相同的js文件,返回我们实际需要的函数内容。
图中,原本的http返回的是3,而我们mock的返回的是6 。而user是一个引入http用来发起请求的文件。然后运行npm run test查看结果,会发现一个很奇怪的事情。
明明我们的结果是不对的,receive了6,但是我们的测试却pass里,中间那一段是node的提示,因为没有加上.catch。
百思不得其解,资料也查了半天,还好我后来弃暗投明,直接开着小飞机投奔谷歌了,度娘里是真的查不到。发现并不是没有加catch的问题,而是使用promise的话,回调函数里必须return一个以.then结尾的promise。
或者,我们也直接使用await/async,也能得到相同的结果,但是无法测试reject的情况,也是有不足的。
对class类的测试 对class的测试,其实与function的测试十分相似,可以new一个实例,对里面的方法,或者属性进行测试。至于要测试一个new Class实例是否创建成功,即测试constructor有没有成功调用的话,官网中有详细的例子,因为感觉意义不大,就不把例子贴出来了。
其他 Jest还可以通过使用 jest.createMockFromModule来模拟我们安装到依赖中的模块,还是用axios举个简单的例子,我们先npm install axios,然后使用jest进行模拟。
在node_modules的同级目录下新建__mocks__,然后使用一个axios.js来模拟axios模块,这里把axios的get换成了一个返回3的函数。
编写测试用例后进行测试,
能够发现use()的返回值就是我们修改axios后get函数返回的3 。
结语:
这篇文章还是磕磕绊绊地写到了这里,然后实在逼不得已给标题后面加了一个(上),至于(下)的话,还没写,反正主要就是jest如何应用于我们具体的框架中的,应该会从vue,react和stencil进行切入。
其实对我来说,这篇文章的心血主要是在这来来回回改了2个多小时的前言里。写文章是我的爱好,而写技术文章更多的是为了学习和工作,希望能把爱好融入到工作和学习中,至少让枯燥的技术文,能够在阅读时更加生动一些。
如果文章对你有帮助,可以关注我的公众号,懒狗小前端