模拟功能

Mock 函数让代码间的联系易于测试,无需真正实现函数,捕获函数调用以及传递的实参,捕获构造函数生成的实例,允许返回值的测试时间配置。

有两种模拟数据的方式:创建模拟函数,在测试代码中使用,或者编写手工模拟数据,覆盖模块依赖。

使用模拟函数

假如我们要测试 forEach 函数,待测代码如下:

function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index])
  }
}

为了测试该函数,我们需要一个模拟函数,并需要检测检测模拟数据的状态,以便确保回调函数按期望结果执行:

const mockCallback = jest.fn()
forEach([0, 1], mockCallback)

// 模拟函数被调用两次
expect(mockCallback.mock.calls.length).toBe(2)

// 第一次调用的第一个参数是 0
expect(mockCallback.mock.calls[0][0]).toBe(0)

// 第二次调用的第一个实参是 1
expect(mockCallback.mock.calls[1][0]).toBe(1)

.mock 属性

所有的 mock 函数都有一个特殊的 .mock 属性,它保存着函数执行的状态数据。.mock 属性也保存着每次调用的 this 指向的对象,因此,也可以探查它:

const myMock = jest.fn()

const a = new myMock()
const b = {}
const bound = myMock.bind(b)
bound()

console.log(myMock.mock.instances)

这些 mock 成员在测试中很有用,可以确保这些函数如何调用,或者实例化:

// 这个函数只被调用一次
expect(someMockFunction.mock.calls.length).toBe(1)

// 第一次调用的第一个参数是 'first arg'
expect(someMockFunction.mock.calls[0][0]).toBe('first arg')

// 第一次调用的第二个实参是 'second arg'
expect(someMockFunction.mock.calls[0][1]).toBe('second arg')

// 函数被实例化两次
expect(someMockFunction.mock.instances.length).toBe(2)

// 第一个实例的 name 属性是 test
expect(someMockFunction.mock.instances[0].name).toEqual('test')

貌似 mock.callsmock.instances 的调用次数(.length)一样?

模拟返回数值

模拟函数还可以用来向代码注入测试数值:

const myMock = jest.fn()
console.log(myMock())

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

console.log(myMock(), myMock(), myMock(), myMock())

Mock 函数在 CPS 风格代码中十分有效。这种风格的代码可以避免复杂的桩数据,用来复现它们代表的组件行为。而是直接注入数值。

const filterTestFn = jest.fn()

filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false)

const result = [11, 12].filter(filterTestFn)

console.log(result) // => 11
console.log(filterTestFn.mock.calls) // => [[11], [12]]

模拟实现

有些场景需要的更多,不仅仅是返回值,而且需要模拟完整的函数实现。这可以通过 mock 函数的 jest.fnmockImplementationOnce 实现。

const myMockFn = jest.fn(cb => cb(null, true))

myMockFn((err, val) => console.log(val))

myMockFn((err, val) => console.log(val))

TO BE CONTINUE...

REF

results matching ""

    No results matching ""