快速入门jest单元测试、mock测试、dom测试、快照测试

 写在前面:本文参考然叔老师的全栈架构成长计划课程中的单元测试部分,对课程学习做了总结。有兴趣的可以去B站搜索“全栈然叔”,能够学习到比较前沿的东西。

一、单元测试

JavaScript 缺少类型检查,编译期间无法定位到错误,单元测试可以帮助你测试多种异常情况。测试可以验证代码的正确性,在上线前做到心里有底。通过 console 虽然可以打印出内部信息,但是这是一次性的事情,下次测试还需要从头来过,效率不能得到保证。通过编写测试用例,可以做到一次编写,多次运行。互联网行业产品迭代速度很快,迭代后必然存在代码重构的过程,那怎么才能保证重构后代码的质量呢?有测试用例做后盾,就可以大胆的进行重构。

简单来说,单元测试做的就是以下事情

  1. 引入要测试的函数
  2. 给函数一个输入
  3. 定义预期输出
  4. 检查函数是否返回了预期的输出结果

即:输入 —— 预期输出 —— 验证结果。

目录

一、单元测试

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类名,再次执行测试可以看到快照测试结果发生了变化。

 快速入门,你入门了吗

猜你喜欢

转载自blog.csdn.net/qq_36384657/article/details/127002200