Jest难点进阶

    科技2022-07-11  86

    Jest难点进阶

    snapshot 快照测试

    快照的使用

    const generateConfig= () => ({ host: 'localhost', port: 3000 }) test('test config snapshot', () => { expect(generateConfig()).toMatchSnapshot() }) test('test another config snapshot', () => { expect(generateAnotherConfig()).toMatchSnapshot() })

    初次执行会生成快照文件,第二次执行同样生成快照文件,并与第一次快照进行比较,快照内容相同则测试通过。

    快照比对失败,可以用 u 键批量更新快照,也可以用 i 键来决定每一个快照的更新操作。

    如果 generateConfig 返回的配置信息在每次函数执行都会发生变化,难道需要我们每次都去更新快照,那么这种情况该如何处理呢?

    比如

    const generateConfig= () => ({ host: 'localhost', port: 3000, time: new Date() })

    这里的 time 字段在每次函数执行的时候都会更新,从而导致快照比对失败,那么能否在进行快照对比的时候自动忽略掉 time 字段的比对呢,其实是可以的

    test('test config snapshot', () => { expect(generateConfig()).toMatchSnapshot({ time: expect.any(Date) }) })

    内联快照

    内联快照-不会生成新的快照文件,而是将快照内容写入当前执行测试的文件中

    其中在生成内联快照的时候需要第三方的包 prettier 配合

    test('test config snapshot', () => { expect(generateConfig()).toMatchInlineSnapshot({ time: expect.any(Date), // 内联快照会放在这里 }) })

    总结:快照测试常用于配置文件的测试、React、Vue组件等的测试

    mock深入学习

    mock 接口请求

    在 Jest基础入门 中实现过接口请求的模拟,如下:

    import axios from 'axios' import { getData } from './api' jest.mock('axios') test.only('test axios getData', async () => { // mock 改变函数的内部实现 axios.get.mockResolvedValue({ data: 'hello' }) await getData().then((response) => { expect(response).toBe('hello') }) })

    这种方法是通过改变 axios 内部方法实现来进行的模拟,这样所有的 get 请求都会被拦截到

    另一种方式是对 getData 方法的 mock

    jest.mock('./api') import { getData } from './api' test.only('test axios getData', async () => { await getData().then((response) => { expect(response).toBe('hello') }) })

    通过mock('./api') 这个文件,在下文导入 getData 这个方法的时候,不再从 './api' 文件中导入,而是去 __mocks__ 文件夹下对应的 api 文件中查找并导入,通过这种方式实现对 getData 方法的拦截处理

    取消mock

    jest.unmock('./api')

    在实际的开发中,无需手动进行 unmock 操作,只需在 jest.config.js 中打开 clearMocks 选项即可,该选项会自动清理 mock 调用

    局部mock

    什么是局部mock,即只针对一个文件中的部分内容进行mock,另一部分保留原始内容,不进行模拟

    比如:

    // util.js import axios from 'axios' export const getConfig = () => { return axios.get('/config') } export const getNumber => () => 12345

    在编写测试用例的时候,只想对 getConfig 方法进行mock,getNumber 使用真实值,不进行mock

    这时就用上了 jest.requireActual 方法

    jest.mock('./util') import { getConfig } from './util' const { getNumber } = jest.requireActual('./util') test('test axios getConfig', async () => { // 从 __mocks__下 util 文件中查找 await getConfig().then((response) => { expect(response).toMatchObject({ success: true }) }) }) test('test getNumber', () => { // 使用真实函数 expect(getNumber()).toEqual(12345) })

    mock timers

    对定时器进行测试

    const timerFunc = (callback) => { setTimeout(() => { callback() }, 3000) } test('test timer', (done) => { timerFunc(() => { expect(1).toBe(1) done() }) })

    如果定时器时间过长,则测试时间也会很长,那么如何mock timer呢

    // mock timer jest.useFakeTimers() test('test timer', () => { timerFunc(() => { const fn = jest.fn() timer(fn) // 立即执行全部定时器 jest.runAllTimers() // jest.runOnlyPendingTimers() 只运行处于已加入队列中的定时器 expect(fn).toHaveBeenCalledTimes(1) }) })

    当不知道该使用 runAllTimers 还是 runOnlyPendingTimers时,可以使用一个API进行替代,就是 advanceTimersByTime,其可以快进指定的时间,来让定时器提前执行,比如

    // 定时器快进 3s 执行 jest.advanceTimersByTime(3000)

    为了保证每一个测试用例 timer 相互隔离,可以将 useFakeTimers 提至 beforeEach 钩子函数中

    beforeEach(() => { jest.useFakeTimers() })

    ES6中类的测试

    对 class 中负责逻辑的测试

    // util.js class Util { init() { } complex() { } } export default Util

    实例化 Util

    let util beforeAll(() => { util = new Util() })

    实际开发中一般对 Util 进行 mock

    // jest mock 发现util是一个类,会自动把类的构造函数和方法变成 jest.fn() jest.mock('./util') import Util from './util' let util beforeAll(() => { util = new Util() }) test('test Util', () => { expect(Util).toHaveBeenCalled() util.complex('complex') expect(Util.mock.instances[0].complex).toHaveBeenCalled() })

    也可以采用自定义mock,在 __mocks__ 下创建 util.js

    const Util = jest.fn(() => { console.log('constructor') }) Util.prototype.complex = ject.fn(() => { console.log('complex') }) export default Util

    自定义mock的另一种方式

    jest.mock('./util', () => { const Util = jest.fn(() => { console.log('constructor') }) Util.prototype.complex = ject.fn(() => { console.log('complex') }) return Util })

    这里推荐在 __mocks__ 文件夹下进行自定义mock

    jest中对DOM节点操作的测试

    创建DOM节点

    import $ from 'jquery' const addDivToBody = () => { $('body').append('<div/>') } test('test dom', () => { addDivToBody() expect($('body').find('div').length).toBe(1) })

    Nodejs不具备dom,jest在node环境中自己模拟了一套dom环境 jsDom

    Processed: 0.036, SQL: 8