Skip to content
Vitest 1
Main Navigation 指南API配置高级
1.6.1
0.34.6

简体中文

English
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

简体中文

English
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

主题

Sidebar Navigation

指南

为什么选择 Vitest

快速开始

特性

工作区

命令行界面

测试筛选

报告器

代码覆盖率

快照(Snapshot)

模拟(Mocking)

类型测试

Vitest UI

浏览器模式

源码内测试

测试上下文

测试环境

扩展匹配器

IDE 集成

调试

与其他测试运行器的比较

迁移指南

常见错误

提升性能

API

测试 API 索引

模拟函数

Vi

expect

expectTypeOf

assert(断言)

assertType

配置

管理 Vitest 配置文件

配置 Vitest

页面导航

expect ​

以下类型在以下类型签名中使用:

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

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

例如,以下代码断言 input 的值等于 2。如果不相等,断言将抛出错误,测试将失败。

ts
import { expect } from 'vitest';

const input = Math.sqrt(4);

expect(input).to.equal(2); // chai API
expect(input).toBe(2); // jest API

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

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

WARNING

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

soft ​

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

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

ts
import { expect, test } from 'vitest';

test('expect.soft test', () => {
  expect.soft(1 + 1).toBe(3); // 标记测试为失败状态并继续执行
  expect.soft(1 + 2).toBe(4); // 标记测试为失败状态并继续执行
});
// 在测试结束时,将输出以上错误。

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

ts
import { expect, test } from 'vitest';

test('expect.soft test', () => {
  expect.soft(1 + 1).toBe(3); // 标记测试为失败状态并继续执行
  expect(1 + 2).toBe(4); // 失败并终止测试,将输出所有先前的错误
  expect.soft(1 + 3).toBe(5); // 不运行
});

WARNING

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

not ​

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

ts
import { expect, test } from 'vitest';

const input = Math.sqrt(16);

expect(input).not.to.equal(2); // chai API
expect(input).not.toBe(2); // jest API

toBe ​

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

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

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

ts
import { expect, test } from 'vitest';

const stock = {
  type: 'apples',
  count: 13,
};

test('stock has 13 apples', () => {
  expect(stock.type).toBe('apples');
  expect(stock.count).toBe(13);
});

test('stocks are the same', () => {
  const refStock = stock; // same reference

  expect(stock).toBe(refStock);
});

尽量避免将 toBe 用于浮点数。因为 JavaScript 会对浮点数进行舍入,所以 0.1 + 0.2 并不完全等于 0.3。要可靠地断言浮点数,请使用 toBeCloseTo 断言。

toBeCloseTo ​

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

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

ts
import { expect, test } from 'vitest';

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

test('decimals are rounded to 5 after the point', () => {
  // 小数点后保留 5 位有效数字
  // 0.2 + 0.1 结果为 0.30000,移除了 "000000000004"
  expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
  // 0.30000000000000004 没有被移除任何内容
  expect(0.2 + 0.1).not.toBeCloseTo(0.3, 50);
});

toBeDefined ​

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

toBeDefined 断言该值不等于 undefined。一个常见的用例是检查函数是否返回了任何值。

ts
import { expect, test } from 'vitest';

function getApples() {
  return 3;
}

test('function returned something', () => {
  expect(getApples()).toBeDefined();
});

toBeUndefined ​

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

与 toBeDefined 相反,toBeUndefined 断言该值为 undefined。一个常见的用例是检查函数是否没有返回任何值。

ts
import { expect, test } from 'vitest';

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

test("mary doesn't have a stock", () => {
  expect(getApplesFromStock('Mary')).toBeUndefined();
});

toBeTruthy ​

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

toBeTruthy 断言该值在转换为布尔值时为 true。如果你不关心具体的值,而只想知道它是否能被转换为 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 中除了 false, null, undefined, NaN, 0, -0, 0n, "" 和 document.all 之外,所有内容都是真值。

toBeFalsy ​

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

toBeFalsy 断言该值在转换为布尔值时为 false。如果你不关心具体的值,而只想知道它是否能被转换为 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 中除了 false, null, undefined, NaN, 0, -0, 0n, "" 和 document.all 之外,所有内容都是真值。

toBeNull ​

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

toBeNull 仅仅断言某个值是否为 null。是 .toBe(null) 的别名。

ts
import { expect, test } from 'vitest';

function apples() {
  return null;
}

test("we don't have apples", () => {
  expect(apples()).toBeNull();
});

toBeNaN ​

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

toBeNaN 仅仅断言某个值是否为 NaN。是 .toBe(NaN) 的别名。

ts
import { expect, test } from 'vitest';

let i = 0;

function getApplesCount() {
  i++;
  return i > 1 ? Number.NaN : i;
}

test('getApplesCount has some unusual side effects...', () => {
  expect(getApplesCount()).not.toBeNaN();
  expect(getApplesCount()).toBeNaN();
});

toBeTypeOf ​

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

toBeTypeOf 用于断言实际值的类型是否与期望的类型相同。

ts
import { expect, test } from 'vitest';

const actual = 'stock';

test('stock is type of string', () => {
  expect(actual).toBeTypeOf('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 用于断言实际值是否等于期望的值,或者,如果它们是对象,则具有相同的结构(递归比较)。你可以在此示例中看到 toEqual 和 toBe 之间的区别:

ts
import { expect, test } from 'vitest';

const stockBill = {
  type: 'apples',
  count: 13,
};

const stockMary = {
  type: 'apples',
  count: 13,
};

test('stocks have the same properties', () => {
  expect(stockBill).toEqual(stockMary);
});

test('stocks are not the same', () => {
  expect(stockBill).not.toBe(stockMary);
});

WARNING

对于 Error 对象,不会执行深度相等比较。只有 Error 对象的 message 属性会被考虑用于相等性比较。要自定义相等性比较以检查 message 以外的属性,请使用 expect.addEqualityTesters。要测试是否抛出了异常,请使用 toThrowError 断言。

toStrictEqual ​

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

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

与 .toEqual 的区别:

  • 检查具有 undefined 属性的键。例如,使用 .toStrictEqual 时,{a: undefined, b: 2} 与 {b: 2} 不匹配。
  • 检查数组稀疏性。例如,使用 .toStrictEqual 时,[, 1] 与 [undefined, 1] 不匹配。
  • 检查对象类型是否相等。例如,具有字段 a 和 b 的类实例将不等于具有字段 a 和 b 的文字对象。
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('水果列表包含 orange', () => {
  expect(getAllFruits()).toContain('orange');

  const element = document.querySelector('#el');
  // element 有一个 class
  expect(element.classList).toContain('flex');
  // element 在另一个 element 内部
  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 { expect, test } from 'vitest';

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

  expect('').not.toHaveLength(3); // 字符串没有长度为 3 的 .length 属性
  expect({ length: 3 }).toHaveLength(3);
});

toHaveProperty ​

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

toHaveProperty 断言对象是否包含指定的 key 属性。

你可以提供一个可选的值参数,该参数类似于 toEqual 匹配器,用于进行深层相等性比较,以验证接收到的属性值。

ts
import { expect, test } from 'vitest';

const invoice = {
  isActive: true,
  'P.O': '12345',
  customer: {
    first_name: 'John',
    last_name: 'Doe',
    location: 'China',
  },
  total_amount: 5000,
  items: [
    {
      type: 'apples',
      quantity: 10,
    },
    {
      type: 'oranges',
      quantity: 5,
    },
  ],
};

test('John Doe Invoice', () => {
  expect(invoice).toHaveProperty('isActive'); // 断言键存在
  expect(invoice).toHaveProperty('total_amount', 5000); // 断言键存在且值相等

  expect(invoice).not.toHaveProperty('account'); // 断言键不存在

  // 使用点符号进行深层引用
  expect(invoice).toHaveProperty('customer.first_name');
  expect(invoice).toHaveProperty('customer.last_name', 'Doe');
  expect(invoice).not.toHaveProperty('customer.location', 'India');

  // 使用包含键的数组进行深层引用
  expect(invoice).toHaveProperty('items[0].type', 'apples');
  expect(invoice).toHaveProperty('items.0.type', 'apples'); // 点符号也有效

  // 使用包含 keyPath 的数组进行深层引用
  expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples');
  expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples'); // 字符串符号也有效

  // 将你的 key 包装在数组中,以避免该键被解析为深层引用
  expect(invoice).toHaveProperty(['P.O'], '12345');
});

toMatch ​

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

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

ts
import { expect, test } from 'vitest';

test('top fruits', () => {
  expect('top fruits include apple, orange and grape').toMatch(/apple/);
  expect('applefruits').toMatch('fruit'); // toMatch 也接受字符串
});

toMatchObject ​

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

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

与 arrayContaining 允许接收的数组包含额外元素不同,如果你需要精确匹配两个数组的元素数量,此方法在需要精确匹配数组元素数量时非常有用。

ts
import { expect, test } from 'vitest';

const johnInvoice = {
  isActive: true,
  customer: {
    first_name: 'John',
    last_name: 'Doe',
    location: 'China',
  },
  total_amount: 5000,
  items: [
    {
      type: 'apples',
      quantity: 10,
    },
    {
      type: 'oranges',
      quantity: 5,
    },
  ],
};

const johnDetails = {
  customer: {
    first_name: 'John',
    last_name: 'Doe',
    location: 'China',
  },
};

test('invoice has john personal details', () => {
  expect(johnInvoice).toMatchObject(johnDetails);
});

test('the number of elements must match exactly', () => {
  // 断言对象的数组匹配
  expect([{ foo: 'bar' }, { baz: 1 }]).toMatchObject([
    { foo: 'bar' },
    { baz: 1 },
  ]);
});

toThrowError ​

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

  • 别名: toThrow

toThrowError 断言函数在调用时是否抛出异常。

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

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

TIP

必须将待测试的代码包裹在一个函数中,否则错误将无法被捕获,导致测试失败。

例如,如果我们想测试 getFruitStock('pineapples') 是否抛出错误,我们可以这样写:

ts
import { expect, test } from 'vitest';

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

  // Do some other stuff
}

test('测试在菠萝上抛出错误', () => {
  // 测试错误消息中包含 "stock":以下是等效的
  expect(() => getFruitStock('pineapples')).toThrowError(/stock/);
  expect(() => getFruitStock('pineapples')).toThrowError('stock');

  // 测试确切的错误消息
  expect(() => getFruitStock('pineapples')).toThrowError(
    /^Pineapples are not in stock$/
  );
});

TIP

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

js
function getAsyncFruitStock() {
  return Promise.reject(new Error('empty'));
}

test('测试在菠萝上抛出错误', async () => {
  await expect(() => getAsyncFruitStock()).rejects.toThrowError('empty');
});

toMatchSnapshot ​

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

这确保值与最新快照匹配。

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

TIP

当快照不匹配并导致测试失败时,如果这种不匹配是预期的,你可以按 u 键来更新快照一次。或者你可以传递 -u 或 --update 命令行选项,使 Vitest 始终更新测试。

ts
import { expect, test } from 'vitest';

test('matches snapshot', () => {
  const data = { foo: new Set(['bar', 'snapshot']) };
  expect(data).toMatchSnapshot();
});

如果只需要测试对象的结构,而不需要完全匹配,可以提供一个对象结构作为参数:

ts
import { expect, test } from 'vitest';

test('matches snapshot', () => {
  const data = { foo: new Set(['bar', 'snapshot']) };
  expect(data).toMatchSnapshot({ foo: expect.any(Set) });
});

toMatchInlineSnapshot ​

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

这确保值与最新快照匹配。

Vitest 会将 inlineSnapshot 字符串参数添加到测试文件的匹配器中(而不是外部的 .snap 文件),并自动更新该参数。

ts
import { expect, test } from 'vitest';

test('matches inline snapshot', () => {
  const data = { foo: new Set(['bar', 'snapshot']) };
  // Vitest 将在更新快照时更新以下内容
  expect(data).toMatchInlineSnapshot(`
    {
      "foo": Set {
        "bar",
        "snapshot",
      },
    }
  `);
});

如果只需要测试对象的结构,而不需要完全匹配,可以提供一个对象结构作为参数:

ts
import { expect, test } from 'vitest';

test('matches snapshot', () => {
  const data = { foo: new Set(['bar', 'snapshot']) };
  expect(data).toMatchInlineSnapshot(
    { foo: expect.any(Set) },
    `
    {
      "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');
});

请注意,由于文件系统操作是异步的,你需要将 await 与 toMatchFileSnapshot() 一起使用。

toThrowErrorMatchingSnapshot ​

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

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

toThrowErrorMatchingInlineSnapshot ​

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

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

toHaveBeenCalled ​

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

此断言用于测试函数是否被调用过。需要将 spy 函数传递给 expect。

ts
import { expect, test, vi } from 'vitest';

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
};

test('spy function', () => {
  const buySpy = vi.spyOn(market, 'buy');

  expect(buySpy).not.toHaveBeenCalled();

  market.buy('apples', 10);

  expect(buySpy).toHaveBeenCalled();
});

toHaveBeenCalledTimes ​

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

此断言用于检查函数被调用的次数。需要将 spy 函数传递给 expect。

ts
import { expect, test, vi } from 'vitest';

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
};

test('spy function called two times', () => {
  const buySpy = vi.spyOn(market, 'buy');

  market.buy('apples', 10);
  market.buy('apples', 20);

  expect(buySpy).toHaveBeenCalledTimes(2);
});

toHaveBeenCalledWith ​

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

此断言用于检查函数是否至少被调用一次,且调用时使用了指定的参数。需要将 spy 函数传递给 expect。

ts
import { expect, test, vi } from 'vitest';

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
};

test('spy function', () => {
  const buySpy = vi.spyOn(market, 'buy');

  market.buy('apples', 10);
  market.buy('apples', 20);

  expect(buySpy).toHaveBeenCalledWith('apples', 10);
  expect(buySpy).toHaveBeenCalledWith('apples', 20);
});

toHaveBeenLastCalledWith ​

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

此断言用于检查函数最后一次调用时是否使用了指定的参数。需要将 spy 函数传递给 expect。

ts
import { expect, test, vi } from 'vitest';

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
};

test('spy function', () => {
  const buySpy = vi.spyOn(market, 'buy');

  market.buy('apples', 10);
  market.buy('apples', 20);

  expect(buySpy).not.toHaveBeenLastCalledWith('apples', 10);
  expect(buySpy).toHaveBeenLastCalledWith('apples', 20);
});

toHaveBeenNthCalledWith ​

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

此断言用于检查函数在第 N 次调用时是否使用了指定的参数。

计数从 1 开始。例如,要检查第二次调用,应使用 .toHaveBeenNthCalledWith(2, ...)。

需要将 spy 函数传递给 expect。

ts
import { expect, test, vi } from 'vitest';

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
};

test('first call of spy function called with right params', () => {
  const buySpy = vi.spyOn(market, 'buy');

  market.buy('apples', 10);
  market.buy('apples', 20);

  expect(buySpy).toHaveBeenNthCalledWith(1, 'apples', 10);
});

toHaveReturned ​

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

此断言用于检查函数是否成功返回过值(即没有抛出错误)。需要将 spy 函数传递给 expect。

ts
import { expect, test, vi } from 'vitest';

function getApplesPrice(amount: number) {
  const PRICE = 10;
  return amount * PRICE;
}

test('spy function returned a value', () => {
  const getPriceSpy = vi.fn(getApplesPrice);

  const price = getPriceSpy(10);

  expect(price).toBe(100);
  expect(getPriceSpy).toHaveReturned();
});

toHaveReturnedTimes ​

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

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

ts
import { expect, test, vi } from 'vitest';

test('spy function returns a value two times', () => {
  const sell = vi.fn((product: string) => ({ product }));

  sell('apples');
  sell('bananas');

  expect(sell).toHaveReturnedTimes(2);
});

toHaveReturnedWith ​

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

你可以调用此断言来检查一个函数是否至少成功返回过一次特定的值。需要将一个监视函数(spy 函数)传递给 expect。

ts
import { expect, test, vi } from 'vitest';

test('spy function returns a product', () => {
  const sell = vi.fn((product: string) => ({ product }));

  sell('apples');

  expect(sell).toHaveReturnedWith({ product: 'apples' });
});

toHaveLastReturnedWith ​

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

你可以调用此断言来检查一个函数是否在其最后一次调用时成功返回了特定的值。需要将一个监视函数(spy 函数)传递给 expect。

ts
import { expect, test, vi } from 'vitest';

test('spy function returns bananas on a last call', () => {
  const sell = vi.fn((product: string) => ({ product }));

  sell('apples');
  sell('bananas');

  expect(sell).toHaveLastReturnedWith({ product: 'bananas' });
});

toHaveNthReturnedWith ​

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

你可以调用此断言来检查一个函数是否在特定调用时成功返回了特定的值。需要将一个监视函数(spy 函数)传递给 expect。

ts
import { expect, test, vi } from 'vitest';

test('spy function returns bananas on second call', () => {
  const sell = vi.fn((product: string) => ({ product }));

  sell('apples');
  sell('bananas');

  expect(sell).toHaveNthReturnedWith(2, { product: 'bananas' });
});

toSatisfy ​

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

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

ts
import { describe, expect, it } from 'vitest';
describe('toSatisfy()', () => {
  const isOdd = (value: number) => value % 2 !== 0;

  it('pass with 0', () => {
    expect(1).toSatisfy(isOdd);
  });

  it('pass with negotiation', () => {
    expect(2).not.toSatisfy(isOdd);
  });
});

resolves ​

  • 类型: Promisify<Assertions>

resolves 旨在简化断言异步代码。使用它可以提取 Promise 成功 resolve 后的值,并使用常规的断言方式来断言该值。如果 Promise 被 rejected,断言将会失败。

它返回相同的 Assertions 对象,但所有 matcher(匹配器)现在都返回 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 现在返回一个 Promise,所以你必须 await 它
  await expect(buyApples()).resolves.toEqual({ id: 1 }); // jest API
  await expect(buyApples()).resolves.to.equal({ id: 1 }); // chai API
});

WARNING

如果未 await 断言,测试将始终通过,导致假阳性。为了确保断言实际上被调用,你可以使用 expect.assertions(number)。

rejects ​

  • 类型: Promisify<Assertions>

rejects 旨在简化断言异步代码。使用它可以提取 Promise 被 rejected 的原因,并使用常规的断言方式来断言该值。如果 Promise 成功 resolve,断言将会失败。

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

例如,如果你有一个函数,当你调用它时会失败,你可以使用以下代码来断言 rejected 的原因:

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 现在返回一个 Promise,所以你必须 await 它
  await expect(buyApples()).rejects.toThrow('no id');
});

WARNING

如果未 await 断言,测试将始终通过,导致假阳性。为了确保断言实际上被调用,你可以使用 expect.assertions(number)。

expect.assertions ​

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

在测试结束后,验证是否调用了特定数量的断言。这在检查异步代码是否被调用时非常有用。

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

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);
});

WARNING

当将 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 => {
    expect(data).toBeTruthy();
  });
  // if not awaited, test will fail
  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。如果你有一个每次都会生成的值,并且你只想知道它是否存在且具有正确的类型,这将非常有用。

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('比较对象属性中的浮点数', () => {
  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 { expect, test } from 'vitest';

test('basket includes fuji', () => {
  const basket = {
    varieties: ['Empire', 'Fuji', 'Gala'],
    count: 3,
  };
  expect(basket).toEqual({
    count: 3,
    varieties: expect.arrayContaining(['Fuji']),
  });
});

TIP

你可以将 expect.not 与此 matcher(匹配器)一起使用,以取反期望值,即数组不包含指定项。

expect.objectContaining ​

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

此不对称匹配器在相等性检查中,如果值具有相似的形状,则返回 true。

ts
import { expect, test } from 'vitest';

test('basket has empire apples', () => {
  const basket = {
    varieties: [
      {
        name: 'Empire',
        count: 1,
      },
    ],
  };
  expect(basket).toEqual({
    varieties: [expect.objectContaining({ name: 'Empire' })],
  });
});

TIP

你可以将 expect.not 与此 matcher(匹配器)一起使用,以取反期望值,即对象不包含指定的属性。

expect.stringContaining ​

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

此不对称匹配器在相等性检查中,如果值为字符串且包含指定子字符串,则返回 true。

ts
import { expect, test } from 'vitest';

test('variety has "Emp" in its name', () => {
  const variety = {
    name: 'Empire',
    count: 1,
  };
  expect(variety).toEqual({
    name: expect.stringContaining('Emp'),
    count: 1,
  });
});

TIP

你可以将 expect.not 与此 matcher(匹配器)一起使用,以取反期望值,即字符串不包含指定的子字符串。

expect.stringMatching ​

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

此不对称匹配器在相等性检查中,如果值为字符串且包含指定子字符串或与正则表达式匹配,则返回 true。

ts
import { expect, test } from 'vitest';

test('variety ends with "re"', () => {
  const variety = {
    name: 'Empire',
    count: 1,
  };
  expect(variety).toEqual({
    name: expect.stringMatching(/re$/),
    count: 1,
  });
});

TIP

你可以将 expect.not 与此 matcher(匹配器)一起使用,以取反期望值,即字符串不匹配指定的正则表达式。

expect.addSnapshotSerializer ​

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

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

添加自定义序列化插件时,应在 setupFiles 中调用此方法。这将影响每个快照。

TIP

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

expect.extend ​

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

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

当你以这种方式定义 matcher 时,你还会创建可以像 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() });
});

TIP

如果你希望你的 matcher 在每个测试中都可用,则应在 setupFiles 中调用此方法。

此函数与 Jest 的 expect.extend 兼容,因此任何使用它来创建自定义 matcher 的库都将与 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 {}
}

WARNING

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

TIP

如果你想了解更多信息,请查看 扩展 matcher 指南。

expect.addEqualityTesters 1.2.0+ ​

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

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

ts
import { expect, test } from 'vitest';

class AnagramComparator {
  public word: string;

  constructor(word: string) {
    this.word = word;
  }

  equals(other: AnagramComparator): boolean {
    const cleanStr1 = this.word.replace(/ /g, '').toLowerCase();
    const cleanStr2 = other.word.replace(/ /g, '').toLowerCase();

    const sortedStr1 = cleanStr1.split('').sort().join('');
    const sortedStr2 = cleanStr2.split('').sort().join('');

    return sortedStr1 === sortedStr2;
  }
}

function isAnagramComparator(a: unknown): a is AnagramComparator {
  return a instanceof AnagramComparator;
}

function areAnagramsEqual(a: unknown, b: unknown): boolean | undefined {
  const isAAnagramComparator = isAnagramComparator(a);
  const isBAnagramComparator = isBAnagramComparator(b);

  if (isAAnagramComparator && isBAnagramComparator) return a.equals(b);
  else if (isAAnagramComparator === isBAnagramComparator) return undefined;
  else return false;
}

expect.addEqualityTesters([areAnagramsEqual]);

test('自定义相等性测试器', () => {
  expect(new AnagramComparator('listen')).toEqual(
    new AnagramComparator('silent')
  );
});
Pager
上一页Vi
下一页expectTypeOf

基于 MIT 许可证 发布。

版权所有 (c) 2024 Mithril Contributors

https://v1.vitest.dev/api/expect

基于 MIT 许可证 发布。

版权所有 (c) 2024 Mithril Contributors