跳至内容

模拟函数

您可以使用 vi.fn 方法创建一个模拟函数来跟踪其执行。如果您想跟踪已创建对象的某个方法,可以使用 vi.spyOn 方法

js
import { vi } from 'vitest'

const fn = vi.fn()
fn('hello world')
fn.mock.calls[0] === ['hello world']

const market = {
  getApples: () => 100
}

const getApplesSpy = vi.spyOn(market, 'getApples')
market.getApples()
getApplesSpy.mock.calls.length === 1

您应该在 expect 上使用模拟断言(例如,toHaveBeenCalled)来断言模拟结果。此 API 参考描述了用于操作模拟行为的可用属性和方法。

getMockImplementation

  • 类型: (...args: any) => any

如果存在,则返回当前模拟实现。

如果模拟是使用 vi.fn 创建的,它将考虑传递下来的方法作为模拟实现。

如果模拟是使用 vi.spyOn 创建的,除非提供了自定义实现,否则它将返回 undefined

getMockName

  • 类型: () => string

使用它来返回使用 .mockName(name) 方法赋予模拟的名称。

mockClear

  • 类型: () => MockInstance

清除有关每次调用的所有信息。调用它后,.mock 上的所有属性将返回空状态。此方法不会重置实现。如果您需要在不同的断言之间清理模拟,它很有用。

如果您希望此方法在每次测试之前自动调用,您可以在配置中启用 clearMocks 设置。

mockName

  • 类型: (name: string) => MockInstance

设置内部模拟名称。如果断言失败,这有助于查看模拟的名称。

mockImplementation

  • 类型: (fn: Function) => MockInstance

接受一个函数,该函数将用作模拟的实现。

ts
const  = .().( =>  + 1)
// or: vi.fn(apples => apples + 1);

const  = (0)
const  = (1)

 === 1 // true
 === 2 // true

..[0][0] === 0 // true
..[1][0] === 1 // true

mockImplementationOnce

  • 类型: (fn: Function) => MockInstance

接受一个函数,该函数将在下一次调用期间用作模拟的实现。可以链接,以便多次函数调用产生不同的结果。

ts
const  = 
  .()
  .(() => true)
  .(() => false)

() // true
() // false

当模拟函数用完实现时,它将调用使用 vi.fn(() => defaultValue).mockImplementation(() => defaultValue) 设置的默认实现(如果它们被调用)。

ts
const  = 
  .(() => 'default')
  .(() => 'first call')
  .(() => 'second call')

// 'first call', 'second call', 'default', 'default'
.((), (), (), ())

withImplementation

  • 类型: (fn: Function, callback: () => void) => MockInstance
  • 类型: (fn: Function, callback: () => Promise<unknown>) => Promise<MockInstance>

在执行回调时临时覆盖原始模拟实现。

js
const  = .(() => 'original')

.(() => 'temp', () => {
  () // 'temp'
})

() // 'original'

可以与异步回调一起使用。必须等待该方法才能在之后使用原始实现。

ts
test('async callback', () => {
  const myMockFn = vi.fn(() => 'original')

  // We await this call since the callback is async
  await myMockFn.withImplementation(
    () => 'temp',
    async () => {
      myMockFn() // 'temp'
    },
  )

  myMockFn() // 'original'
})

请注意,此方法优先于 mockImplementationOnce

mockRejectedValue

  • 类型: (value: any) => MockInstance

接受一个错误,该错误将在调用异步函数时被拒绝。

ts
const  = .().(new ('Async error'))

await () // throws "Async error"

mockRejectedValueOnce

  • 类型: (value: any) => MockInstance

接受一个值,该值将在下一次函数调用期间被拒绝。如果链接,每次连续调用都将拒绝指定的值。

ts
const  = 
  .()
  .('first call')
  .(new ('Async error'))

await () // first call
await () // throws "Async error"

mockReset

  • 类型: () => MockInstance

执行 mockClear 的操作,并将内部实现设为空函数(在调用时返回 undefined)。这也将重置所有“一次”实现。当您想将模拟完全重置为默认状态时,这很有用。

如果您希望此方法在每次测试之前自动调用,您可以在配置中启用 mockReset 设置。

mockRestore

  • 类型: () => MockInstance

执行 mockReset 的操作,并将内部实现恢复为原始函数。

请注意,从 vi.fn() 恢复模拟将把实现设置为返回 undefined 的空函数。恢复 vi.fn(impl) 将把实现恢复为 impl

如果您希望此方法在每次测试之前自动调用,您可以在配置中启用 restoreMocks 设置。

mockResolvedValue

  • 类型: (value: any) => MockInstance

接受一个值,该值将在调用异步函数时被解析。

ts
const  = .().(42)

await () // 42

mockResolvedValueOnce

  • 类型: (value: any) => MockInstance

接受一个值,该值将在下一次函数调用期间被解析。如果链接,每次连续调用都将解析指定的值。

ts
const  = 
  .()
  .('default')
  .('first call')
  .('second call')

await () // first call
await () // second call
await () // default
await () // default

mockReturnThis

  • 类型: () => MockInstance

如果您需要从方法中返回 this 上下文而不调用实际实现,请使用此方法。这是以下内容的简写

ts
spy.mockImplementation(function () {
  return this
})

mockReturnValue

  • 类型: (value: any) => MockInstance

接受一个值,该值将在每次调用模拟函数时返回。

ts
const  = .()
.(42)
() // 42
.(43)
() // 43

mockReturnValueOnce

  • 类型: (value: any) => MockInstance

接受一个值,该值将在下一次函数调用期间返回。如果链接,每次连续调用都将返回指定的值。

如果没有更多 mockReturnValueOnce 值可用,模拟将回退到先前定义的实现(如果有)。

ts
const  = 
  .()
  .('default')
  .('first call')
  .('second call')

// 'first call', 'second call', 'default', 'default'
.((), (), (), ())

mock.calls

这是一个数组,包含每次调用的所有参数。数组的一个项目是该调用的参数。

js
const fn = vi.fn()

fn('arg1', 'arg2')
fn('arg3')

fn.mock.calls === [
  ['arg1', 'arg2'], // first call
  ['arg3'], // second call
]

mock.lastCall

这包含最后一次调用的参数。如果模拟未被调用,将返回 undefined

mock.results

这是一个数组,包含从函数中 返回 的所有值。数组的一个项目是一个具有 typevalue 属性的对象。可用的类型是

  • 'return' - 函数在不抛出异常的情况下返回。
  • 'throw' - 函数抛出一个值。

value 属性包含返回的值或抛出的错误。如果函数返回一个 Promise,则 value 将是解析的值,而不是实际的 Promise,除非它从未被解析。

js
const fn = vi.fn()
  .mockReturnValueOnce('result')
  .mockImplementationOnce(() => { throw new Error('thrown error') })

const result = fn() // returned 'result'

try {
  fn() // threw Error
}
catch {}

fn.mock.results === [
  // first result
  {
    type: 'return',
    value: 'result',
  },
  // last result
  {
    type: 'throw',
    value: Error,
  },
]

mock.invocationCallOrder

模拟执行的顺序。这将返回一个数字数组,这些数字在所有定义的模拟之间共享。

js
const fn1 = vi.fn()
const fn2 = vi.fn()

fn1()
fn2()
fn1()

fn1.mock.invocationCallOrder === [1, 3]
fn2.mock.invocationCallOrder === [2]

mock.instances

这是一个数组,包含使用 new 关键字调用模拟时实例化的所有实例。请注意,这是一个实际的函数上下文 (this),而不是返回值。

警告

如果模拟是使用 new MyClass() 实例化的,那么 mock.instances 将是一个包含一个值的数组

js
const MyClass = vi.fn()
const a = new MyClass()

MyClass.mock.instances[0] === a

如果您从构造函数中返回一个值,它将不会出现在instances数组中,而是出现在results中。

js
const Spy = vi.fn(() => ({ method: vi.fn() }))
const a = new Spy()

Spy.mock.instances[0] !== a
Spy.mock.results[0] === a