学习 Jest - Mock

模拟函数

允许除去函数实际实现,捕获对函数的调用以及在调用中传递的参数

使用 new 实例化时捕获构造函数的实例及允许在测试时模拟返回值

// 为提供的数组每个项提供回调
function forEach(items, callback) {
    for (let i = 0; i < items.length; i++) {
        callback(items[i])
    }
}

const mockCB = jest.fn(x => 42 + x)

forEach([0, 1], mockCB)

// 模拟函数被调用 2 次
expect(mockCB.mock.calls.length).toBe(2)
// 对函数的第一次调用的第一个参数 是 0
expect(mockCB.mock.calls[0][0]).toBe(0)
// 对函数的第二次调用的第一个参数 是 1
expect(mockCB.mock.calls[1][0]).toBe(1)
// 第一次调用返回值是 42
expect(mockCB.mock.results[0].value).toBe(42)

// jest.spyOn
// 会实际执行mock函数
test('spyOn', async () => {
    const spyFn = jest.spyOn(ImportObj, 'ImportObjFn')
    // 调用 ImportObj 的 ImportObjFn 方法
})

.mock 原型

所有模拟函数都有此属性

此属性保存有关如何调用函数及返回的函数的数据

还会跟踪每个调用的值

const mockFn = jest.fn()
mockFn.mock.[fieldName]

// 只调用一次函数
expect(mockFn.mock.calls.length).toBe(1)
// 第一次调用的第一个参数是first
expect(mockFn.mock.calls[0][0]).toBe('first')
// 第一次调用的第二个参数时second
expect(mockFn.mock.calls[0][1]).toBe('second')
// 第一次调用返回值是 ret
expect(mockFn.mock.results[0].value).toBe('ret')
// 特定 this 上下文中调用的 Element 对象
expect(mockFn.mock.contexts[0]).toBe(Element)
// 正好实例化了两次
expect(mockFn.mock.instances.length).toBe(2)
// 第一个实例化返回的对象属性 name 是 test
expect(mockFn.mock.instances[0].name).toBe('test')
// 最后一次调用的第一个参数时 test
expect(mockFn.mock.lastCall[0]).toBe('test')
// ...

模拟返回值

const mockFn = jest.fn()

mock.mockReturnValueOnce(10)
    .mockReturnValueOnce('x')
    .mockReturnValue(true)

mock() // 10
mock() // x
mock() // true
mock() // true

模拟模块

// user.js
import axios from 'axios'

export class Users {
    static all() {
        return axios.get('./users.json').then(res => res.data)
    }
}

// user.test.js
import axios from 'axios'
import { Users } from './Users'

jest.mock('axios')
test('Mock Module', () => {
    const data = []
    const res = {data: 'a'}
    axios.get.mockResolvedValue(res)

    return Users.all().then(data => expect(data).toBe(data))
})

模拟对象部分

// 可以模拟模块的子集部分,其余部分可以保留实现

// example.js
export const foo = 'f'
export const bar = () => 'b'
export default () => 'z'

// example.test.js
import DefaultExport, { bar, foo } from './example'

jest.mock('./example', () => {
    const originalModule = jest.requireActual('./example')

    // 模拟 默认导出 和 foo 导出
    return {
        __esModule: true,
        ...originalModule,
        default: jest.fn(() => 'm'),
        foo: 'm f'
    }
})

test('Module Partials Mock', () => {
    const defaultExportResult = defaultExport()
    expect(defaultExportResult).toBe('m')
    // 已执行过
    expect(defaultExportResult).toHaveBeenCalled()
})

模拟实现

const MFn = jest.fn(cb => cb(null, true))
MFn((err,val) => console.log(val)) // true

// foo.js
modules.exports = () => {
    // do somethings...
}

// foo.test.js
jest.mock('./foo')
const foo = require('./foo')

// 模拟实现
foo.mockImplementation(() => 42)
foo() // 42

// 多次调用不同值
// 用完定义的实现后会使用默认实现
const MFn = jest
    .fn(() => 'de')
    .mockImplementationOnce(cb => cb(null, true))
    .mockImplementationOnce(cb => cb(null, false))

MFn((err,val) => console.log(val)) // true
MFn((err,val) => console.log(val)) // false
MFn((err,val) => console.log(val)) // de
MFn((err,val) => console.log(val)) // de

获取 this

const Obj = {
    Fn: jest.fn().mockReturnThis()
}
// or
const Obj = {
    Fn: jest.fn(function () {
        return this
    })
}

模拟名称

const Fn = jest
    .fn()
    .mockReturnValue('d')
    .mockImplementation(scalar => 42 + scalar)
    .mockName('add42')

学习 Jest - Mock
http://localhost:8080/archives/dc394101-c293-42cf-9bea-22533480caaa
作者
inksha
发布于
2024年09月14日
许可协议