写在前面:本文参考然叔老师的全栈架构成长计划课程中的单元测试部分,对课程学习做了总结。有兴趣的可以去B站搜索“全栈然叔”,能够学习到比较前沿的东西。
一、单元测试
JavaScript 缺少类型检查,编译期间无法定位到错误,单元测试可以帮助你测试多种异常情况。测试可以验证代码的正确性,在上线前做到心里有底。通过 console 虽然可以打印出内部信息,但是这是一次性的事情,下次测试还需要从头来过,效率不能得到保证。通过编写测试用例,可以做到一次编写,多次运行。互联网行业产品迭代速度很快,迭代后必然存在代码重构的过程,那怎么才能保证重构后代码的质量呢?有测试用例做后盾,就可以大胆的进行重构。
简单来说,单元测试做的就是以下事情
- 引入要测试的函数
- 给函数一个输入
- 定义预期输出
- 检查函数是否返回了预期的输出结果
即:输入 —— 预期输出 —— 验证结果。
目录
一、单元测试
1.1 一个Jest Demo
1.2 jest断言函数
jest分组函数
jest 的钩子函数
jest 钩子函数的作用域
二、异步测试
一个异步测试的demo
新建src/delay.js文件
新建src/__test__/delay.spec.js测试文件
测试
提升异步测试效率
更改delay.spec.js
三、Mock测试
一个Mock测试的Demo
新增src/fetch.js
新增src/__test__/fetch.spec.js
测试
jest的fn方法
四、Dom测试
dom.js文件
dom.spec.js测试文件
安装jsdom
新增jsdom-config.js配置文件
修改dom.spec.js文件
五、快照测试
新增snapshot.spec.js测试文件
测试
1.1 一个Jest Demo
jest 是facebook开源的一套js测试框架。中文文档:快速开始 · Jest
多刷刷官方文档,官方文档是最好的教程。
Jest 适用但不局限于使用以下技术的项目:Babel,、TypeScript、 Node、 React、Angular、Vue 等。jest规定每个代码里都有一套test目录,这个test目录对应相应的测试函数。test文件夹下某个文件(test.spec.js)与src文件夹下文件同名文件test.js是对应的映射关系。将实际代码与测试文件隔离放置,方便管理维护。
1、新建vue项目
初始化项目,生成package.json文件。
命令:npm init -y
2、安装 Jest
推荐全局安装,jest默认工作在当前工作区的npm 包下
命令:npm i jest -g
vscode安装jest类型提示,可以提示jest语法
npm i -D @types/jest
3、新增加法程序
src/add.js
新建src文件夹,新增add.js文件
const add = (a,b) => a+b;
module.exports = add;
4、新增测试类
src同级文件下新增__test__文件夹,并创建同名文件add.spec.js
//引入add方法
const add = require('../add')
//describe测试单元,用于分组
describe('',()=>{
//比如,第一个测试用例,测试1+2是否为3
//test第一个参数打印一段文本,第二个参数执行回调函数
test('add(1,2) === 3',()=>{
//断言 期待执行add(1,2)后返回结果是3,tobe表示期待的结果值
expect(add(1,2)).toBe(3)
})
test('add(2,2)===4',()=>{
expect(add(2,2).toBe(4))
})
})
//第一个测试用例
上面的测试代码就像自然语言说话一样,很舒服。expect 称为断言函数:断定一个真实的结果是期望的结果。很多测试框架都有这个方法。
5、执行jest
直接在当前目录下执行jest,可以将所有的测试用例都执行一遍。
命令:test
1.2 jest断言函数
测试即运行结果是否与我们预期结果一致 断言函数用来验证结果是否正确
常用的断言方法
.toBe对值的判断
.toEqual类似于===全等判断
expect(运行结果).toBe(期望的结果);
//常见断言方法
expect({a:1}).toBe({a:1})//判断两个对象是否相等
expect(1).not.toBe(2)//判断不等
expect({ a: 1, foo: { b: 2 } }).toEqual({ a: 1, foo: { b: 2 } })
expect(n).toBeNull(); //判断是否为null
expect(n).toBeUndefined(); //判断是否为undefined
expect(n).toBeDefined(); //判断结果与toBeUndefined相反
expect(n).toBeTruthy(); //判断结果为true
expect(n).toBeFalsy(); //判断结果为false
expect(value).toBeGreaterThan(3); //大于3
expect(value).toBeGreaterThanOrEqual(3.5); //大于等于3.5
expect(value).toBeLessThan(5); //小于5
expect(value).toBeLessThanOrEqual(4.5); //小于等于4.5
expect(value).toBeCloseTo(0.3); // 浮点数判断相等
expect('Christoph').toMatch(/stop/); //正则表达式判断
expect(['one','two']).toContain('one'); //不解释
jest分组函数
describe("关于每个功能或某个组件的单元测试",()=>{
// 不同用例的单元测试
})
jest 的钩子函数
beforeAll:在所有测试用例运行之前,会先调用 beforeAll 钩子函数
beforeEach:每个测试用例执行之前,都会调用,类似 vue-router 的 beforeEach,这样每次测试都是一个全新的实例,各个用例之间互不干扰。
afterEach:与 beforeEach 相反
afterAll:与 beforeAll 相反
jest 钩子函数的作用域
describe:把增加相关的代码放到一类分组中,相减的放到另一类分组中,可以使用 describe 分组 ,Jest 默认会在最外层套一个 describe 不要在 describe 中写初始化的代码,避免踩坑,一定要写到钩子函数里
二、异步测试
如果测试函数中存在异步操作,且需要异步操作执行完才进行断言结果,单元测试函数需要设置done形参,在定时回调函数中调用。
一个异步测试的demo
新建src/delay.js文件
函数 delay表示延迟的意思,用延迟执行测试一下异步
module.exports = fn => {
//设置1秒钟之后执行该延迟函数
setTimeout(()=>fn(),1000);
};
新建src/__test__/delay.spec.js测试文件
it方法中的done函数表示程序执行结束,以此为标识进行测试
//引入 delay函数
const delay = require('../delay')
//异步测试
//done函数表示程序执行完成
it('异步测试',done => {
delay(() =>{
//等待定时器一定时间后调用delay函数执行
done()
})
expect(true).toBe(true)
})
测试
命令:jest delay
提升异步测试效率
由于异步测试会根据程序中的等待时间而进行等待,可以通过jest.useFakeTimers()方法提升效率。简单来说就是模拟一个时间函数,让时间函数进行一个快进的处理。
更改delay.spec.js
//引入 delay函数
const delay = require('../delay')
//异步测试
//done函数表示程序执行完成
it('异步测试',done => {
//.useFakeTimers()使用虚拟时钟,在执行过程中进行拨快
jest.useFakeTimers()
delay(() =>{
//等待定时器一定时间后调用delay函数执行
done()
})
//.runAllTimers()在调用完成后,拨快时钟
jest.runAllTimers()
expect(true).toBe(true)
})
命令:jest delay
可以看到测试时间得到了显著的提示
三、Mock测试
vue项目开发中调用其他库信息时,就不是单元测试了。
mock函数相当于制造了一个虚拟函数
一个Mock测试的Demo
安装axios
命令:npm i axios -s
新增src/fetch.js
const axios = require('axios')
//定义一个getData方法,通过axios.get方法获取某个接口数据
exports.getData = () => axios.get('/abc/bcd');
新增src/__test__/fetch.spec.js
const {getData} = require('../fetch')
const axios = require('axios')
//mock方法
jest.mock('axios')
it('测试fetch',async()=>{
//根据行为模拟mock对象
//假设已知fetch.js中的axios.get方法返回一个promise对象值为'123'
axios.get.mockResolvedValueOnce('123')
axios.get.mockResolvedValueOnce('456')
const data1 = await getData()
const data2 = await getData()
//断言结果'123'
expect(data1).toBe('123')
expect(data2).toBe('456')
})
测试
jest fetch
如果返回的值与预期不符
jest会进行提示,expect期待的值是什么,receive接收到的值是什么
jest的fn方法
jest.fn()是创建Mock函数最简单的方式,如果没有定义函数内部的实现,jest.fn()会返回undefined作为返回值
在上面异步测试的方法中新增一个测试用例,检测mockFn是否被调用
//引入 delay函数
const delay = require('../delay')
//异步测试
//done函数表示程序执行完成
it('异步测试',done => {
//.useFakeTimers()使用虚拟时钟,在执行过程中进行拨快
jest.useFakeTimers()
delay(() =>{
//等待定时器一定时间后调用delay函数执行
done()
})
//.runAllTimers()在调用完成后,拨快时钟
jest.runAllTimers()
expect(true).toBe(true)
})
it('异步测试 fn',done => {
//定义一个mockFn函数,该函数本身可以被断言
let mockFn = jest.fn()
jest.useFakeTimers()
delay(() =>{
mockFn()
done()
})
jest.runAllTimers()
expect(true).toBe(true)
//断言mockFn是否被断言
expect(mockFn).toBeCalled()
})
执行 jest delay后成功
如果将mockFn()调用注释掉,执行报错
四、Dom测试
所谓Dom测试是为了验证前端程序对Dom的操作是否正确。为了测试方便,又不希望在浏览器环境中进行这时就可以在Node环境中进行,但是Node中并没有Dom模型。解决办法就是使用jsdom进行Dom的仿真。
dom.js文件
//创建一个div的元素
exports.generateDiv = () => {
const div = document.createElement('div')
//修改div的属性
div.className = 'c1'
//添加到文档对象模型中
document.body.appendChild(div)
}
dom.spec.js测试文件
const {generateDiv} = require('../dom')
it('Dom测试',()=>{
generateDiv()
})
命令:jest dom
jest dom测试结果
直接运行dom测试案例会报错
安装jsdom
根据报错提示使用jsdom工具测试Dom元素
命令:npm i jsdom -s
新增jsdom-config.js配置文件
//引入jsdom
const jsdom = require('jsdom') // eslint-disable-line
const { JSDOM } = jsdom
//jsdom实现对真实dom方针
const dom = new JSDOM('<!DOCTYPE html><head/><body></body>', {
url: 'http://localhost/',
referrer: 'https://example.com/',
contentType: 'text/html',
userAgent: 'Mellblomenator/9000',
includeNodeLocations: true,
storageQuota: 10000000,
})
//浏览器包括window对象和document对象
//将dom绑定到全局 dom对象的window属性绑定到全局的window属性
global.window = dom.window
global.document = window.document
global.navigator = window.navigator
修改dom.spec.js文件
引入jsdom-config.js文件,重新执行测试
命令:jest dom
可以发现测试正常通过
五、快照测试
快照测试使用jest的 toMatchSnapshot 匹配器,运行后会在根目录生成一个 __snapshots__ 文件夹。
修改内容后与上次生成的快照进行比较,执行jest --updateSnapshot 或 jest -u 可更新快照
执行toMatchInlineSnapshot 会将快照生成到行内
快照测试其实是将对象实例进行序列化,转成文件的形式进行持久化保存。等到下次测试的时候与之比较。快照测试广泛应用于ui测试。就比如说在前端测试中,可以Dom对象做成快照。每当你想要确保你的UI不会有意外的改变,快照测试是非常有用的工具。典型的做法是在渲染了UI组件之后,保存一个快照文件, 检测他是否与保存在单元测试旁的快照文件相匹配。 若两个快照不匹配,测试将失败:有可能做了意外的更改,或者UI组件已经更新到了新版本。
接下来我们使用快照测试测试Dom元素
新增snapshot.spec.js测试文件
//引入dom
const {generateDiv} = require('../dom')
//引入jsdom-config
require('../jsdom-config')
//快照测试
test('Dom的快照测试',()=>{
generateDiv()
//toMatchSnapshot 和上次的快照进行比对
expect(document.getElementsByClassName('c1')).toMatchSnapshot()
})
测试
命令:test snapshot
执行后可以看到__test__目录下新增了一个snapshots文件。这里就是存放的快照文件
再次测试时快照文件不更新
命令:test snapshot
假设修改dom.js文件中的div类名,再次执行测试可以看到快照测试结果发生了变化。
文章来源:https://uudwc.com/A/5aOz
快速入门,你入门了吗文章来源地址https://uudwc.com/A/5aOz