跳至内容

expect

以下类型用于下面的类型签名

ts
type Awaitable<T> = T | PromiseLike<T>

expect 用于创建断言。在此上下文中,断言 是可以调用的函数来断言一个语句。Vitest 默认提供 chai 断言,并且还基于 chai 构建了与 Jest 兼容的断言。

例如,这段代码断言 input 值等于 2。如果不是,断言将抛出错误,测试将失败。

ts
import {  } from 'vitest'

const  = .(4)

()..(2) // chai API
().(2) // jest API

从技术上讲,此示例没有使用 test 函数,因此您将在控制台中看到 Node.js 错误而不是 Vitest 输出。要了解有关 test 的更多信息,请阅读 测试 API 参考

此外,expect 可以静态地用于访问匹配器函数(稍后描述)等等。

警告

expect 对测试类型没有影响,如果表达式没有类型错误。如果您想将 Vitest 用作 类型检查器,请使用 expectTypeOfassertType

soft

  • 类型: ExpectStatic & (actual: any) => Assertions

expect.soft 的功能类似于 expect,但它不会在断言失败时终止测试执行,而是继续运行并将失败标记为测试失败。测试期间遇到的所有错误都将显示,直到测试完成。

ts
import { ,  } from 'vitest'

('expect.soft test', () => {
  .(1 + 1).(3) // mark the test as fail and continue
  .(1 + 2).(4) // mark the test as fail and continue
})
// At the end of the test, the above errors will be output.

它也可以与 expect 一起使用。如果 expect 断言失败,测试将被终止,所有错误都将被显示。

ts
import { ,  } from 'vitest'

('expect.soft test', () => {
  .(1 + 1).(3) // mark the test as fail and continue
  (1 + 2).(4) // failed and terminate the test, all previous errors will be output
  .(1 + 3).(5) // do not run
})

警告

expect.soft 只能在 test 函数内使用。

not

使用 not 将否定断言。例如,这段代码断言 input 值不等于 2。如果相等,断言将抛出错误,测试将失败。

ts
import { ,  } from 'vitest'

const  = .(16)

()...(2) // chai API
()..(2) // jest API

toBe

  • 类型: (value: any) => Awaitable<void>

toBe 可用于断言基本类型是否相等,或者对象是否共享相同的引用。它等效于调用 expect(Object.is(3, 3)).toBe(true)。如果对象不同,但您想检查它们的结构是否相同,可以使用 toEqual

例如,下面的代码检查交易者是否有 13 个苹果。

ts
import { ,  } from 'vitest'

const  = {
  : 'apples',
  : 13,
}

('stock has 13 apples', () => {
  (.).('apples')
  (.).(13)
})

('stocks are the same', () => {
  const  =  // same reference

  ().()
})

尽量不要将 toBe 用于浮点数。由于 JavaScript 对它们进行了舍入,因此 0.1 + 0.2 并不严格等于 0.3。要可靠地断言浮点数,请使用 toBeCloseTo 断言。

toBeCloseTo

  • 类型: (value: number, numDigits?: number) => Awaitable<void>

使用 toBeCloseTo 比较浮点数。可选的 numDigits 参数限制了要检查的小数点后数字的数量。例如

ts
import { ,  } from 'vitest'

.('decimals are not equal in javascript', () => {
  (0.2 + 0.1).(0.3) // 0.2 + 0.1 is 0.30000000000000004
})

('decimals are rounded to 5 after the point', () => {
  // 0.2 + 0.1 is 0.30000 | "000000000004" removed
  (0.2 + 0.1).(0.3, 5)
  // nothing from 0.30000000000000004 is removed
  (0.2 + 0.1)..(0.3, 50)
})

toBeDefined

  • 类型: () => Awaitable<void>

toBeDefined 断言该值不等于 undefined。有用的用例是检查函数是否返回了任何内容。

ts
import { ,  } from 'vitest'

function () {
  return 3
}

('function returned something', () => {
  (()).()
})

toBeUndefined

  • 类型: () => Awaitable<void>

toBeDefined 相反,toBeUndefined 断言该值等于 undefined。有用的用例是检查函数是否没有返回任何内容。

ts
import { ,  } from 'vitest'

function (: string) {
  if ( === 'Bill')
    return 13
}

('mary doesn\'t have a stock', () => {
  (('Mary')).()
})

toBeTruthy

  • 类型: () => Awaitable<void>

toBeTruthy 断言该值转换为布尔值时为真。如果您不关心该值,但只想了解它是否可以转换为 true,这很有用。

例如,有了这段代码,您就不关心 stocks.getInfo 的返回值——它可能是复杂的对象、字符串或任何其他东西。代码仍然可以工作。

ts
import { Stocks } from './stocks.js'

const stocks = new Stocks()
stocks.sync('Bill')
if (stocks.getInfo('Bill'))
  stocks.sell('apples', 'Bill')

因此,如果您想测试 stocks.getInfo 是否为真值,您可以编写

ts
import { expect, test } from 'vitest'
import { Stocks } from './stocks.js'

const stocks = new Stocks()

test('if we know Bill stock, sell apples to him', () => {
  stocks.sync('Bill')
  expect(stocks.getInfo('Bill')).toBeTruthy()
})

JavaScript 中的所有内容都是真值,除了 falsenullundefinedNaN0-00n""document.all

toBeFalsy

  • 类型: () => Awaitable<void>

toBeFalsy 断言该值转换为布尔值时为假。如果您不关心该值,但只想了解它是否可以转换为 false,这很有用。

例如,有了这段代码,您就不关心 stocks.stockFailed 的返回值——它可能会返回任何假值,但代码仍然可以工作。

ts
import { Stocks } from './stocks.js'

const stocks = new Stocks()
stocks.sync('Bill')
if (!stocks.stockFailed('Bill'))
  stocks.sell('apples', 'Bill')

因此,如果您想测试 stocks.stockFailed 是否为假值,您可以编写

ts
import { expect, test } from 'vitest'
import { Stocks } from './stocks.js'

const stocks = new Stocks()

test('if Bill stock hasn\'t failed, sell apples to him', () => {
  stocks.syncStocks('Bill')
  expect(stocks.stockFailed('Bill')).toBeFalsy()
})

JavaScript 中的所有内容都是真值,除了 falsenullundefinedNaN0-00n""document.all

toBeNull

  • 类型: () => Awaitable<void>

toBeNull 只是断言某物是否为 null.toBe(null) 的别名。

ts
import { ,  } from 'vitest'

function () {
  return null
}

('we don\'t have apples', () => {
  (()).()
})

toBeNaN

  • 类型: () => Awaitable<void>

toBeNaN 只是断言某物是否为 NaN.toBe(NaN) 的别名。

ts
import { ,  } from 'vitest'

let  = 0

function () {
  ++
  return  > 1 ? . : 
}

('getApplesCount has some unusual side effects...', () => {
  (())..()
  (()).()
})

toBeTypeOf

  • 类型: (c: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => Awaitable<void>

toBeTypeOf 断言实际值是否为接收类型的类型。

ts
import { ,  } from 'vitest'

const  = 'stock'

('stock is type of string', () => {
  ().('string')
})

toBeInstanceOf

  • 类型: (c: any) => Awaitable<void>

toBeInstanceOf 断言实际值是否为接收类的实例。

ts
import { expect, test } from 'vitest'
import { Stocks } from './stocks.js'

const stocks = new Stocks()

test('stocks are instance of Stocks', () => {
  expect(stocks).toBeInstanceOf(Stocks)
})

toBeGreaterThan

  • 类型: (n: number | bigint) => Awaitable<void>

toBeGreaterThan 断言实际值是否大于接收的值。相等的值将使测试失败。

ts
import { expect, test } from 'vitest'
import { getApples } from './stocks.js'

test('have more then 10 apples', () => {
  expect(getApples()).toBeGreaterThan(10)
})

toBeGreaterThanOrEqual

  • 类型: (n: number | bigint) => Awaitable<void>

toBeGreaterThanOrEqual 断言实际值是否大于接收的值或等于它。

ts
import { expect, test } from 'vitest'
import { getApples } from './stocks.js'

test('have 11 apples or more', () => {
  expect(getApples()).toBeGreaterThanOrEqual(11)
})

toBeLessThan

  • 类型: (n: number | bigint) => Awaitable<void>

toBeLessThan 断言实际值是否小于接收的值。相等的值将使测试失败。

ts
import { expect, test } from 'vitest'
import { getApples } from './stocks.js'

test('have less then 20 apples', () => {
  expect(getApples()).toBeLessThan(20)
})

toBeLessThanOrEqual

  • 类型: (n: number | bigint) => Awaitable<void>

toBeLessThanOrEqual 断言实际值是否小于接收的值或等于它。

ts
import { expect, test } from 'vitest'
import { getApples } from './stocks.js'

test('have 11 apples or less', () => {
  expect(getApples()).toBeLessThanOrEqual(11)
})

toEqual

  • 类型: (received: any) => Awaitable<void>

toEqual 断言实际值是否等于接收的值,或者如果它是对象,则具有相同的结构(递归地比较它们)。您可以在此示例中看到 toEqualtoBe 之间的区别

ts
import { ,  } from 'vitest'

const  = {
  : 'apples',
  : 13,
}

const  = {
  : 'apples',
  : 13,
}

('stocks have the same properties', () => {
  ().()
})

('stocks are not the same', () => {
  ()..()
})

警告

对于 Error 对象,不会执行深度相等。只有 Errormessage 属性被认为是相等的。要自定义相等以检查除 message 之外的属性,请使用 expect.addEqualityTesters。要测试是否抛出了某物,请使用 toThrowError 断言。

toStrictEqual

  • 类型: (received: any) => Awaitable<void>

toStrictEqual 断言实际值是否等于接收的值,或者如果它是对象,则具有相同的结构(递归地比较它们),并且类型相同。

.toEqual 的区别

  • 具有 undefined 属性的键将被检查。例如,{a: undefined, b: 2} 在使用 .toStrictEqual 时与 {b: 2} 不匹配。
  • 将检查数组稀疏性。例如,[, 1] 在使用 .toStrictEqual 时与 [undefined, 1] 不匹配。
  • 将检查对象类型是否相等。例如,具有字段 ab 的类实例将不等于具有字段 ab 的文字对象。
ts
import { expect, test } from 'vitest'

class Stock {
  constructor(type) {
    this.type = type
  }
}

test('structurally the same, but semantically different', () => {
  expect(new Stock('apples')).toEqual({ type: 'apples' })
  expect(new Stock('apples')).not.toStrictEqual({ type: 'apples' })
})

toContain

  • 类型: (received: string) => Awaitable<void>

toContain 断言实际值是否在数组中。toContain 还可以检查字符串是否是另一个字符串的子字符串。从 Vitest 1.0 开始,如果您在类似浏览器的环境中运行测试,此断言还可以检查类是否包含在 classList 中,或者元素是否在另一个元素内。

ts
import { expect, test } from 'vitest'
import { getAllFruits } from './stocks.js'

test('the fruit list contains orange', () => {
  expect(getAllFruits()).toContain('orange')

  const element = document.querySelector('#el')
  // element has a class
  expect(element.classList).toContain('flex')
  // element is inside another one
  expect(document.querySelector('#wrapper')).toContain(element)
})

toContainEqual

  • 类型: (received: any) => Awaitable<void>

toContainEqual 断言具有特定结构和值的项目是否包含在数组中。它在每个元素内部的工作方式类似于 toEqual

ts
import { expect, test } from 'vitest'
import { getFruitStock } from './stocks.js'

test('apple available', () => {
  expect(getFruitStock()).toContainEqual({ fruit: 'apple', count: 5 })
})

toHaveLength

  • 类型: (received: number) => Awaitable<void>

toHaveLength 断言对象是否具有 .length 属性,并且它是否设置为某个数值。

ts
import { ,  } from 'vitest'

('toHaveLength', () => {
  ('abc').(3)
  ([1, 2, 3]).(3)

  ('')..(3) // doesn't have .length of 3
  ({ : 3 }).(3)
})

toHaveProperty

  • 类型: (key: any, received?: any) => Awaitable<void>

toHaveProperty 断言对象在提供的引用 key 处是否存在属性。

您还可以提供一个可选的值参数,也称为深度相等,就像 toEqual 匹配器一样,用于比较接收到的属性值。

ts
import { ,  } from 'vitest'

const  = {
  'isActive': true,
  'P.O': '12345',
  'customer': {
    : 'John',
    : 'Doe',
    : 'China',
  },
  'total_amount': 5000,
  'items': [
    {
      : 'apples',
      : 10,
    },
    {
      : 'oranges',
      : 5,
    },
  ],
}

('John Doe Invoice', () => {
  ().('isActive') // assert that the key exists
  ().('total_amount', 5000) // assert that the key exists and the value is equal

  ()..('account') // assert that this key does not exist

  // Deep referencing using dot notation
  ().('customer.first_name')
  ().('customer.last_name', 'Doe')
  ()..('customer.location', 'India')

  // Deep referencing using an array containing the key
  ().('items[0].type', 'apples')
  ().('items.0.type', 'apples') // dot notation also works

  // Deep referencing using an array containing the keyPath
  ().(['items', 0, 'type'], 'apples')
  ().(['items', '0', 'type'], 'apples') // string notation also works

  // Wrap your key in an array to avoid the key from being parsed as a deep reference
  ().(['P.O'], '12345')
})

toMatch

  • 类型: (received: string | regexp) => Awaitable<void>

toMatch 断言字符串是否与正则表达式或字符串匹配。

ts
import { ,  } from 'vitest'

('top fruits', () => {
  ('top fruits include apple, orange and grape').(/apple/)
  ('applefruits').('fruit') // toMatch also accepts a string
})

toMatchObject

  • 类型: (received: object | array) => Awaitable<void>

toMatchObject 断言对象是否与对象的属性子集匹配。

您也可以传递一个对象数组。如果您想检查两个数组在元素数量上是否匹配,这很有用,而不是 arrayContaining,它允许接收数组中存在额外的元素。

ts
import { ,  } from 'vitest'

const  = {
  : true,
  : {
    : 'John',
    : 'Doe',
    : 'China',
  },
  : 5000,
  : [
    {
      : 'apples',
      : 10,
    },
    {
      : 'oranges',
      : 5,
    },
  ],
}

const  = {
  : {
    : 'John',
    : 'Doe',
    : 'China',
  },
}

('invoice has john personal details', () => {
  ().()
})

('the number of elements must match exactly', () => {
  // Assert that an array of object matches
  ([{ : 'bar' }, { : 1 }]).([
    { : 'bar' },
    { : 1 },
  ])
})

toThrowError

  • 类型: (received: any) => Awaitable<void>

  • 别名: toThrow

toThrowError 断言函数在被调用时是否抛出错误。

您可以提供一个可选参数来测试是否抛出了特定错误

  • 正则表达式:错误消息与模式匹配
  • 字符串:错误消息包含子字符串

提示

您必须将代码包装在函数中,否则错误将不会被捕获,测试将失败。

例如,如果我们想测试 getFruitStock('pineapples') 是否抛出,我们可以编写

ts
import { ,  } from 'vitest'

function (: string) {
  if ( === 'pineapples')
    throw new ('Pineapples are not in stock')

  // Do some other stuff
}

('throws on pineapples', () => {
  // Test that the error message says "stock" somewhere: these are equivalent
  (() => ('pineapples')).(/stock/)
  (() => ('pineapples')).('stock')

  // Test the exact error message
  (() => ('pineapples')).(
    /^Pineapples are not in stock$/,
  )
})

提示

要测试异步函数,请与 rejects 结合使用。

js
function () {
  return .(new ('empty'))
}

test('throws on pineapples', async () => {
  await expect(() => ()).rejects.toThrowError('empty')
})

toMatchSnapshot

  • 类型: <T>(shape?: Partial<T> | string, message?: string) => void

这确保了一个值与最新的快照匹配。

您可以提供一个可选的 hint 字符串参数,该参数将附加到测试名称。虽然 Vitest 始终在快照名称末尾附加一个数字,但简短的描述性提示可能比数字更有用,可以区分单个 it 或测试块中的多个快照。Vitest 在相应的 .snap 文件中按名称对快照进行排序。

提示

当快照不匹配并导致测试失败时,如果预期不匹配,您可以按 u 键更新一次快照。或者,您可以传递 -u--update CLI 选项,使 Vitest 始终更新测试。

ts
import { ,  } from 'vitest'

('matches snapshot', () => {
  const  = { : new (['bar', 'snapshot']) }
  ().()
})

您也可以提供一个对象的形状,如果您只是测试一个对象的形状,并且不需要它与 100% 兼容。

ts
import { ,  } from 'vitest'

('matches snapshot', () => {
  const  = { : new (['bar', 'snapshot']) }
  ().({ : .() })
})

toMatchInlineSnapshot

  • 类型: <T>(shape?: Partial<T> | string, snapshot?: string, message?: string) => void

这确保了一个值与最新的快照匹配。

Vitest 将 inlineSnapshot 字符串参数添加到测试文件中的匹配器中(而不是外部 .snap 文件)。

ts
import { ,  } from 'vitest'

('matches inline snapshot', () => {
  const  = { : new (['bar', 'snapshot']) }
  // Vitest will update following content when updating the snapshot
  ().(`
    {
      "foo": Set {
        "bar",
        "snapshot",
      },
    }
  `)
})

您也可以提供一个对象的形状,如果您只是测试一个对象的形状,并且不需要它与 100% 兼容。

ts
import { ,  } from 'vitest'

('matches snapshot', () => {
  const  = { : new (['bar', 'snapshot']) }
  ().(
    { : .() },
    `
    {
      "foo": Any<Set>,
    }
  `
  )
})

toMatchFileSnapshot 0.30.0+

  • 类型: <T>(filepath: string, message?: string) => Promise<void>

将快照与显式指定的文件(而不是 .snap 文件)的内容进行比较或更新。

ts
import { expect, it } from 'vitest'

it('render basic', async () => {
  const result = renderHTML(h('div', { class: 'foo' }))
  await expect(result).toMatchFileSnapshot('./test/basic.output.html')
})

请注意,由于文件系统操作是异步的,因此您需要使用 awaittoMatchFileSnapshot() 一起使用。

toThrowErrorMatchingSnapshot

  • 类型: (message?: string) => void

toMatchSnapshot 相同,但期望与 toThrowError 相同的值。

toThrowErrorMatchingInlineSnapshot

  • 类型: (snapshot?: string, message?: string) => void

toMatchInlineSnapshot 相同,但期望与 toThrowError 相同的值。

toHaveBeenCalled

  • 类型: () => Awaitable<void>

此断言对于测试函数是否已被调用很有用。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

const  = {
  (: string, : number) {
    // ...
  },
}

('spy function', () => {
  const  = .(, 'buy')

  ()..()

  .('apples', 10)

  ().()
})

toHaveBeenCalledTimes

  • 类型: (amount: number) => Awaitable<void>

此断言检查函数是否被调用了一定次数。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

const  = {
  (: string, : number) {
    // ...
  },
}

('spy function called two times', () => {
  const  = .(, 'buy')

  .('apples', 10)
  .('apples', 20)

  ().(2)
})

toHaveBeenCalledWith

  • 类型: (...args: any[]) => Awaitable<void>

此断言检查函数是否至少被调用了一次,并且使用某些参数。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

const  = {
  (: string, : number) {
    // ...
  },
}

('spy function', () => {
  const  = .(, 'buy')

  .('apples', 10)
  .('apples', 20)

  ().('apples', 10)
  ().('apples', 20)
})

toHaveBeenLastCalledWith

  • 类型: (...args: any[]) => Awaitable<void>

此断言检查函数是否在其最后一次调用中使用某些参数被调用。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

const  = {
  (: string, : number) {
    // ...
  },
}

('spy function', () => {
  const  = .(, 'buy')

  .('apples', 10)
  .('apples', 20)

  ()..('apples', 10)
  ().('apples', 20)
})

toHaveBeenNthCalledWith

  • 类型: (time: number, ...args: any[]) => Awaitable<void>

此断言检查函数是否在特定时间使用某些参数被调用。计数从 1 开始。因此,要检查第二个条目,您将编写 .toHaveBeenNthCalledWith(2, ...)

需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

const  = {
  (: string, : number) {
    // ...
  },
}

('first call of spy function called with right params', () => {
  const  = .(, 'buy')

  .('apples', 10)
  .('apples', 20)

  ().(1, 'apples', 10)
})

toHaveReturned

  • 类型: () => Awaitable<void>

此断言检查函数是否至少成功返回了一次值(即没有抛出错误)。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

function (: number) {
  const  = 10
  return  * 
}

('spy function returned a value', () => {
  const  = .()

  const  = (10)

  ().(100)
  ().()
})

toHaveReturnedTimes

  • 类型: (amount: number) => Awaitable<void>

此断言检查函数是否成功返回了一定次数的值(即没有抛出错误)。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

('spy function returns a value two times', () => {
  const  = .((: string) => ({  }))

  ('apples')
  ('bananas')

  ().(2)
})

toHaveReturnedWith

  • 类型: (returnValue: any) => Awaitable<void>

您可以调用此断言来检查函数是否至少成功返回了一次具有某些参数的值。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

('spy function returns a product', () => {
  const  = .((: string) => ({  }))

  ('apples')

  ().({ : 'apples' })
})

toHaveLastReturnedWith

  • 类型: (returnValue: any) => Awaitable<void>

您可以调用此断言来检查函数是否在其最后一次调用中成功返回了具有某些参数的值。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

('spy function returns bananas on a last call', () => {
  const  = .((: string) => ({  }))

  ('apples')
  ('bananas')

  ().({ : 'bananas' })
})

toHaveNthReturnedWith

  • 类型: (time: number, returnValue: any) => Awaitable<void>

您可以调用此断言来检查函数是否在特定调用中成功返回了具有某些参数的值。需要将间谍函数传递给 expect

ts
import { , ,  } from 'vitest'

('spy function returns bananas on second call', () => {
  const  = .((: string) => ({  }))

  ('apples')
  ('bananas')

  ().(2, { : 'bananas' })
})

toSatisfy

  • 类型: (predicate: (value: any) => boolean) => Awaitable<void>

此断言检查一个值是否满足某个谓词。

ts
import { , ,  } from 'vitest'
('toSatisfy()', () => {
  const  = (: number) =>  % 2 !== 0

  ('pass with 0', () => {
    (1).()
  })

  ('pass with negation', () => {
    (2)..()
  })
})

resolves

  • 类型: Promisify<Assertions>

resolves 旨在在断言异步代码时消除样板代码。使用它从挂起的 promise 中解包值,并使用通常的断言断言其值。如果 promise 被拒绝,则断言将失败。

它返回相同的 Assertions 对象,但所有匹配器现在都返回 Promise,因此您需要 await 它。也适用于 chai 断言。

例如,如果您有一个函数,它进行 API 调用并返回一些数据,您可以使用此代码来断言其返回值

ts
import { expect, test } from 'vitest'

async function buyApples() {
  return fetch('/buy/apples').then(r => r.json())
}

test('buyApples returns new stock id', async () => {
  // toEqual returns a promise now, so you HAVE to await it
  await expect(buyApples()).resolves.toEqual({ id: 1 }) // jest API
  await expect(buyApples()).resolves.to.equal({ id: 1 }) // chai API
})

警告

如果断言没有被等待,那么您将有一个每次都会通过的假阳性测试。为了确保断言实际上被调用,您可以使用 expect.assertions(number)

rejects

  • 类型: Promisify<Assertions>

rejects 旨在在断言异步代码时消除样板代码。使用它来解包 promise 被拒绝的原因,并使用通常的断言断言其值。如果 promise 成功解析,则断言将失败。

它返回相同的 Assertions 对象,但所有匹配器现在都返回 Promise,因此您需要 await 它。也适用于 chai 断言。

例如,如果您有一个函数,当您调用它时会失败,您可以使用此代码来断言原因

ts
import { expect, test } from 'vitest'

async function buyApples(id) {
  if (!id)
    throw new Error('no id')
}

test('buyApples throws an error when no id provided', async () => {
  // toThrow returns a promise now, so you HAVE to await it
  await expect(buyApples()).rejects.toThrow('no id')
})

警告

如果断言没有被等待,那么您将有一个每次都会通过的假阳性测试。为了确保断言实际上被调用,您可以使用 expect.assertions(number)

expect.assertions

  • 类型: (count: number) => void

在测试通过或失败后,验证在测试期间调用了一定数量的断言。一个有用的情况是检查异步代码是否被调用。

例如,如果我们有一个异步调用两个匹配器的函数,我们可以断言它们实际上被调用了。

ts
import { expect, test } from 'vitest'

async function doAsync(...cbs) {
  await Promise.all(
    cbs.map((cb, index) => cb({ index })),
  )
}

test('all assertions are called', async () => {
  expect.assertions(2)
  function callback1(data) {
    expect(data).toBeTruthy()
  }
  function callback2(data) {
    expect(data).toBeTruthy()
  }

  await doAsync(callback1, callback2)
})

警告

在使用 assertions 进行异步并发测试时,必须使用本地 测试上下文 中的 expect 来确保检测到正确的测试。

expect.hasAssertions

  • 类型: () => void

在测试通过或失败后,验证在测试期间至少调用了一个断言。一个有用的情况是检查异步代码是否被调用。

例如,如果您有一段代码调用回调,我们可以在回调中进行断言,但如果我们不检查断言是否被调用,测试将始终通过。

ts
import { expect, test } from 'vitest'
import { db } from './db.js'

const cbs = []

function onSelect(cb) {
  cbs.push(cb)
}

// after selecting from db, we call all callbacks
function select(id) {
  return db.select({ id }).then((data) => {
    return Promise.all(
      cbs.map(cb => cb(data)),
    )
  })
}

test('callback was called', async () => {
  expect.hasAssertions()
  onSelect((data) => {
    // should be called on select
    expect(data).toBeTruthy()
  })
  // if not awaited, test will fail
  // if you don't have expect.hasAssertions(), test will pass
  await select(3)
})

expect.unreachable

  • 类型: (message?: string) => never

此方法用于断言永远不应该到达一行。

例如,如果我们想测试 build() 由于接收到的目录没有 src 文件夹而抛出异常,并且还分别处理每个错误,我们可以这样做

ts
import { expect, test } from 'vitest'

async function build(dir) {
  if (dir.includes('no-src'))
    throw new Error(`${dir}/src does not exist`)
}

const errorDirs = [
  'no-src-folder',
  // ...
]

test.each(errorDirs)('build fails with "%s"', async (dir) => {
  try {
    await build(dir)
    expect.unreachable('Should not pass build')
  }
  catch (err: any) {
    expect(err).toBeInstanceOf(Error)
    expect(err.stack).toContain('build')

    switch (dir) {
      case 'no-src-folder':
        expect(err.message).toBe(`${dir}/src does not exist`)
        break
      default:
        // to exhaust all error tests
        expect.unreachable('All error test must be handled')
        break
    }
  }
})

expect.anything

  • 类型: () => any

此非对称匹配器,当与相等性检查一起使用时,将始终返回 true。有用,如果您只是想确保属性存在。

ts
import { expect, test } from 'vitest'

test('object has "apples" key', () => {
  expect({ apples: 22 }).toEqual({ apples: expect.anything() })
})

expect.any

  • 类型: (constructor: unknown) => any

此非对称匹配器,当与相等性检查一起使用时,只有当值为指定构造函数的实例时才返回 true。有用,如果您有一个每次都会生成的 value,并且您只想了解它是否存在于适当的类型中。

ts
import { expect, test } from 'vitest'
import { generateId } from './generators.js'

test('"id" is a number', () => {
  expect({ id: generateId() }).toEqual({ id: expect.any(Number) })
})

expect.closeTo 1.0.0+

  • 类型: (expected: any, precision?: number) => any

expect.closeTo 在比较对象属性或数组项中的浮点数时很有用。如果您需要比较一个数字,请改用 .toBeCloseTo

可选的 numDigits 参数限制了小数点后要检查的位数。对于默认值 2,测试标准是 Math.abs(expected - received) < 0.005 (即 10 ** -2 / 2)

例如,此测试以 5 位精度通过

js
test('compare float in object properties', () => {
  expect({
    title: '0.1 + 0.2',
    sum: 0.1 + 0.2,
  }).toEqual({
    title: '0.1 + 0.2',
    sum: expect.closeTo(0.3, 5),
  })
})

expect.arrayContaining

  • 类型: <T>(expected: T[]) => any

当与相等性检查一起使用时,此非对称匹配器将返回 true,如果该值是一个数组并且包含指定的项目。

ts
import { ,  } from 'vitest'

('basket includes fuji', () => {
  const  = {
    : [
      'Empire',
      'Fuji',
      'Gala',
    ],
    : 3
  }
  ().({
    : 3,
    : .(['Fuji'])
  })
})

提示

您可以使用 expect.not 与此匹配器一起使用来否定预期值。

expect.objectContaining

  • 类型: (expected: any) => any

当与相等性检查一起使用时,此非对称匹配器将返回 true,如果该值具有类似的形状。

ts
import { ,  } from 'vitest'

('basket has empire apples', () => {
  const  = {
    : [
      {
        : 'Empire',
        : 1,
      }
    ],
  }
  ().({
    : [
      .({ : 'Empire' }),
    ]
  })
})

提示

您可以使用 expect.not 与此匹配器一起使用来否定预期值。

expect.stringContaining

  • 类型: (expected: any) => any

当与相等性检查一起使用时,此非对称匹配器将返回 true,如果该值是一个字符串并且包含指定的子字符串。

ts
import { ,  } from 'vitest'

('variety has "Emp" in its name', () => {
  const  = {
    : 'Empire',
    : 1,
  }
  ().({
    : .('Emp'),
    : 1,
  })
})

提示

您可以使用 expect.not 与此匹配器一起使用来否定预期值。

expect.stringMatching

  • 类型: (expected: any) => any

当与相等性检查一起使用时,此非对称匹配器将返回 true,如果该值是一个字符串并且包含指定的子字符串,或者如果该字符串与正则表达式匹配。

ts
import { ,  } from 'vitest'

('variety ends with "re"', () => {
  const  = {
    : 'Empire',
    : 1,
  }
  ().({
    : .(/re$/),
    : 1,
  })
})

提示

您可以使用 expect.not 与此匹配器一起使用来否定预期值。

expect.addSnapshotSerializer

  • 类型: (plugin: PrettyFormatPlugin) => void

此方法添加自定义序列化器,这些序列化器在创建快照时被调用。这是一个高级功能 - 如果你想了解更多,请阅读 关于自定义序列化器的指南

如果你正在添加自定义序列化器,你应该在 setupFiles 中调用此方法。这将影响每个快照。

提示

如果你之前使用过 Vue CLI with Jest,你可能想安装 jest-serializer-vue。否则,你的快照将被包装在一个字符串中,这会导致 " 被转义。

expect.extend

  • 类型: (matchers: MatchersObject) => void

你可以用你自己的匹配器扩展默认匹配器。此函数用于用自定义匹配器扩展匹配器对象。

当你以这种方式定义匹配器时,你也会创建非对称匹配器,这些匹配器可以像 expect.stringContaining 一样使用。

ts
import { expect, test } from 'vitest'

test('custom matchers', () => {
  expect.extend({
    toBeFoo: (received, expected) => {
      if (received !== 'foo') {
        return {
          message: () => `expected ${received} to be foo`,
          pass: false,
        }
      }
    },
  })

  expect('foo').toBeFoo()
  expect({ foo: 'foo' }).toEqual({ foo: expect.toBeFoo() })
})

提示

如果你想让你的匹配器出现在每个测试中,你应该在 setupFiles 中调用此方法。

此函数与 Jest 的 expect.extend 兼容,因此任何使用它来创建自定义匹配器的库都可以在 Vitest 中使用。

如果你正在使用 TypeScript,从 Vitest 0.31.0 开始,你可以在环境声明文件(例如:vitest.d.ts)中使用以下代码扩展默认的 Assertion 接口

ts
interface CustomMatchers<R = unknown> {
  toBeFoo: () => R
}

declare module 'vitest' {
  interface Assertion<T = any> extends CustomMatchers<T> {}
  interface AsymmetricMatchersContaining extends CustomMatchers {}
}

警告

不要忘记在你的 tsconfig.json 中包含环境声明文件。

提示

如果你想了解更多,请查看 关于扩展匹配器的指南

expect.addEqualityTesters 1.2.0+

  • 类型: (tester: Array<Tester>) => void

你可以使用此方法来定义自定义测试器,这些测试器是匹配器使用的方法,用于测试两个对象是否相等。它与 Jest 的 expect.addEqualityTesters 兼容。

ts
import { ,  } from 'vitest'

class  {
  public : string

  constructor(: string) {
    this. = 
  }

  (: ): boolean {
    const  = this..(/ /g, '').()
    const  = ..(/ /g, '').()

    const  = .('').().('')
    const  = .('').().('')

    return  === 
  }
}

function (: unknown):  is  {
  return  instanceof 
}

function (: unknown, : unknown): boolean | undefined {
  const  = ()
  const  = ()

  if ( && )
    return .()

  else if ( === )
    return 

  else
    return false
}

.([])

('custom equality tester', () => {
  (new ('listen')).(new ('silent'))
})