Skip to content
Vitest 3
Main Navigation 指南 & API配置瀏覽器模式進階 API
3.2.0
2.1.9
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

快速入門

功能特色

配置參考

API

測試 API 參考

模擬函式

Vi

expect

expectTypeOf

assert

assertType

指南

命令列介面

測試篩選

測試專案

報告器

程式碼覆蓋率

快照

模擬(Mocking)

平行化

型別測試

Vitest UI

內聯測試

測試上下文

測試註解

測試環境

擴展匹配器

IDE 整合

偵錯

常見錯誤

遷移指南

遷移到 Vitest 3.0

從 Jest 遷移

效能

測試效能分析

提升效能

瀏覽器模式

進階 API

與其他測試執行器的比較

本頁導覽

expect ​

以下類型在下面的類型簽章中被使用。

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

expect 用於建立斷言。在此情境中,斷言 是可以呼叫來判斷陳述是否為真的函式。Vitest 預設提供 chai 斷言,以及基於 chai 建構的 Jest 相容斷言。與 Jest 不同,Vitest 支援將訊息作為第二個參數——如果斷言失敗,錯誤訊息將會是該訊息。

ts
export interface ExpectStatic
  extends Chai.ExpectStatic,
    AsymmetricMatchersContaining {
  <T>(actual: T, message?: string): Assertion<T>;
  extend: (expects: MatchersObject) => void;
  anything: () => any;
  any: (constructor: unknown) => any;
  getState: () => MatcherState;
  setState: (state: Partial<MatcherState>) => void;
  not: AsymmetricMatchersContaining;
}

例如,以下程式碼斷言 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 函式內部使用。

poll ​

ts
interface ExpectPoll extends ExpectStatic {
  (actual: () => T, options: { interval; timeout; message }): Promise<
    Assertions<T>
  >;
}

expect.poll 會重新執行 斷言 直到成功。您可以透過設定 interval 和 timeout 選項來配置 Vitest 應該重新執行 expect.poll 回呼的次數。

如果在 expect.poll 回呼內部拋出錯誤,Vitest 將再次重試,直到逾時。

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

test('element exists', async () => {
  asyncInjectElement();

  await expect.poll(() => document.querySelector('.element')).toBeTruthy();
});

WARNING

expect.poll 使每個斷言都成為非同步的,因此您需要 await 它。從 Vitest 3 開始,如果您忘記 await 它,測試將失敗並發出警告。

expect.poll 不適用於多個匹配器:

  • 不支援快照匹配器,因為它們總是會成功。如果您的條件不穩定,請考慮改用 vi.waitFor 來先解決它:
ts
import { expect, vi } from 'vitest';

const flakyValue = await vi.waitFor(() => getFlakyValue());
expect(flakyValue).toMatchSnapshot();
  • 不支援 .resolves 和 .rejects。如果條件是異步的,expect.poll 已經 await 了該條件。
  • 不支援 toThrow 及其別名,因為 expect.poll 條件總是在匹配器獲取值之前解析。

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; // 相同的引用

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

test('decimals are rounded to 5 after the point', () => {
  // 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 沒有庫存', () => {
  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('我們沒有蘋果', () => {
  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 具有一些不尋常的副作用...', () => {
  expect(getApplesCount()).not.toBeNaN();
  expect(getApplesCount()).toBeNaN();
});

toBeOneOf ​

  • 類型: (sample: Array<any>) => any

toBeOneOf 斷言值是否與所提供陣列中的任一值匹配。

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

test('fruit is one of the allowed values', () => {
  expect(fruit).toBeOneOf(['apple', 'banana', 'orange']);
});

非對稱匹配器在測試可選屬性(其值可以是 null 或 undefined)時特別有用:

ts
test('optional properties can be null or undefined', () => {
  const user = {
    firstName: 'John',
    middleName: undefined,
    lastName: 'Doe',
  };

  expect(user).toEqual({
    firstName: expect.any(String),
    middleName: expect.toBeOneOf([expect.any(String), undefined]),
    lastName: expect.any(String),
  });
});

TIP

您可以將 expect.not 與此匹配器一起使用,以確保值不符合任何給定的選項。

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 物件,也會比較不可列舉的屬性,例如 name、message、cause 和 AggregateError.errors。對於 Error.cause,比較方式為非對稱:

ts
// 成功
expect(new Error('hi', { cause: 'x' })).toEqual(new Error('hi'));

// 失敗
expect(new Error('hi')).toEqual(new Error('hi', { cause: 'x' }));

要測試是否拋出錯誤,請使用 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 也可以檢查一個字串是否為另一個字串的子字串。如果您在類似瀏覽器的環境中執行測試,此斷言還可以檢查類別是否包含在 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');
  // 元素有一個類別
  expect(element.classList).toContain('flex');
  // 元素在另一個元素內部
  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); // 沒有 .length 為 3
  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'); // 點符號也適用

  // 使用包含鍵路徑的陣列進行深度引用
  expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples');
  expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples'); // 字串符號也適用

  // 將鍵包裝在陣列中,以避免鍵被解析為深度引用
  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 斷言函式在被呼叫時是否拋出錯誤。

您可以提供一個可選參數來測試是否拋出特定的錯誤:

  • RegExp:錯誤訊息匹配該模式
  • string:錯誤訊息包含子字串
  • Error、AsymmetricMatcher:與所接收的物件進行比較,類似於 toEqual(received)

TIP

您必須將程式碼包裝在函式中,否則錯誤將不會被捕獲,測試將失敗。

這不適用於非同步呼叫,因為 rejects 正確地解開了 Promise:

ts
test('expect rejects toThrow', async ({ expect }) => {
  const promise = Promise.reject(new Error('Test'));
  await expect(promise).rejects.toThrowError();
});

例如,如果我們想測試 getFruitStock('pineapples') 是否拋出錯誤,我們可以這樣寫:

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

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

  // 執行其他操作
}

test('throws on pineapples', () => {
  // 測試錯誤訊息中某處包含「stock」:這些是等效的
  expect(() => getFruitStock('pineapples')).toThrowError(/stock/);
  expect(() => getFruitStock('pineapples')).toThrowError('stock');

  // 測試確切的錯誤訊息
  expect(() => getFruitStock('pineapples')).toThrowError(
    /^Pineapples are not in stock$/
  );

  expect(() => getFruitStock('pineapples')).toThrowError(
    new Error('Pineapples are not in stock')
  );
  expect(() => getFruitStock('pineapples')).toThrowError(
    expect.objectContaining({
      message: 'Pineapples are not in stock',
    })
  );
});

TIP

要測試非同步函式,請結合 rejects 使用。

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

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

toMatchSnapshot ​

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

這確保值與最新的快照相符。

您可以提供一個可選的 hint 字串參數,該參數會附加到測試名稱後。儘管 Vitest 總是在快照名稱的末尾附加一個數字,但簡短的描述性提示可能比數字更有用,以便在單個 it 或 test 區塊中區分多個快照。Vitest 會在相應的 .snap 檔案中按名稱排序快照。

TIP

當快照不相符並導致測試失敗時,如果預期不相符,您可以按 u 鍵更新快照一次。或者您可以傳遞 -u 或 --update CLI 選項,使 Vitest 始終更新測試。

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

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

您也可以提供物件的形狀,如果您只是測試物件的形狀,並且不需要它 100% 相容:

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, hint?: 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",
      },
    }
  `);
});

您也可以提供物件的形狀,如果您只是測試物件的形狀,並且不需要它 100% 相容:

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 ​

  • 類型: <T>(filepath: string, hint?: 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() 一起使用。如果未使用 await,Vitest 會將其視為 expect.soft,這表示即使快照不相符,語句後的程式碼也會繼續執行。測試完成後,Vitest 將檢查快照,如果存在不相符,則會失敗。

toThrowErrorMatchingSnapshot ​

  • 類型: (hint?: string) => void

與 toMatchSnapshot 相同,但預期與 toThrowError 相同的值。

toThrowErrorMatchingInlineSnapshot ​

  • 類型: (snapshot?: string, hint?: string) => void

與 toMatchInlineSnapshot 相同,但預期與 toThrowError 相同的值。

toHaveBeenCalled ​

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

此斷言用於測試函式是否已被呼叫。需要將間諜函式傳遞給 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>

此斷言檢查函式是否被呼叫了特定的次數。需要將間諜函式傳遞給 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>

此斷言檢查函式是否至少被呼叫過一次,並帶有特定的參數。需要將間諜函式傳遞給 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);
});

toHaveBeenCalledBefore 3.0.0+ ​

  • 類型:(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable<void>

此斷言檢查 Mock 是否在另一個 Mock 之前被呼叫。

ts
test('calls mock1 before mock2', () => {
  const mock1 = vi.fn();
  const mock2 = vi.fn();

  mock1();
  mock2();
  mock1();

  expect(mock1).toHaveBeenCalledBefore(mock2);
});

toHaveBeenCalledAfter 3.0.0+ ​

  • 類型:(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable<void>

此斷言檢查 Mock 是否在另一個 Mock 之後被呼叫。

ts
test('calls mock1 after mock2', () => {
  const mock1 = vi.fn();
  const mock2 = vi.fn();

  mock2();
  mock1();
  mock2();

  expect(mock1).toHaveBeenCalledAfter(mock2);
});

toHaveBeenCalledExactlyOnceWith 3.0.0+ ​

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

此斷言檢查函式是否只被呼叫一次,並帶有特定的參數。需要將間諜函式傳遞給 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);

  expect(buySpy).toHaveBeenCalledExactlyOnceWith('apples', 10);
});

toHaveBeenLastCalledWith ​

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

此斷言檢查函式在上次呼叫時是否帶有特定的參數。需要將間諜函式傳遞給 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>

此斷言檢查函式是否在特定時間被呼叫,並帶有特定的參數。計數從 1 開始。因此,要檢查第二個條目,您將寫 .toHaveBeenNthCalledWith(2, ...)。

需要將間諜函式傳遞給 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>

此斷言檢查函式是否至少成功返回一次值(即,沒有拋出錯誤)。需要將間諜函式傳遞給 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>

此斷言檢查函式是否成功返回了確切次數的值(即,沒有拋出錯誤)。需要將間諜函式傳遞給 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>

您可以呼叫此斷言來檢查函式是否至少成功返回一次帶有特定參數的值。需要將間諜函式傳遞給 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>

您可以呼叫此斷言來檢查函式在上次呼叫時是否成功返回了特定值。需要將間諜函式傳遞給 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>

您可以呼叫此斷言來檢查函式是否在特定呼叫時成功返回了帶有特定參數的值。需要將間諜函式傳遞給 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' });
});

toHaveResolved ​

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

此斷言檢查函式是否至少成功解析一次值(即,沒有拒絕)。需要將間諜函式傳遞給 expect。

如果函式返回了一個 Promise,但尚未解析,則此斷言將失敗。

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

async function getApplesPrice(amount: number) {
  return amount * (await db.get('price'));
}

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

  const price = await getPriceSpy(10);

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

toHaveResolvedTimes ​

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

此斷言檢查函式是否成功解析了確切次數的值(即,沒有拒絕)。需要將間諜函式傳遞給 expect。

這只會計算已解析的 Promise。如果函式返回了一個 Promise,但尚未解析,則不會計數。

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

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

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

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

toHaveResolvedWith ​

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

您可以呼叫此斷言來檢查函式是否至少成功解析一次特定值。需要將間諜函式傳遞給 expect。

如果函式返回了一個 Promise,但尚未解析,則此斷言將失敗。

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

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

  await sell('apples');

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

toHaveLastResolvedWith ​

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

您可以呼叫此斷言來檢查函式在上次呼叫時是否成功解析了特定值。需要將間諜函式傳遞給 expect。

如果函式返回了一個 Promise,但尚未解析,則此斷言將失敗。

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

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

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

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

toHaveNthResolvedWith ​

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

您可以呼叫此斷言來檢查函式是否在特定呼叫時成功解析了特定值。需要將間諜函式傳遞給 expect。

如果函式返回了一個 Promise,但尚未解析,則此斷言將失敗。

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

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

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

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

toSatisfy ​

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

此斷言檢查值是否滿足特定謂詞。

ts
import { describe, expect, it } from 'vitest';

const isOdd = (value: number) => value % 2 !== 0;

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

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

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 現在返回一個 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)。

從 Vitest 3 開始,如果方法未被 await,Vitest 將在測試結束時顯示警告。在 Vitest 4 中,如果斷言未被 await,測試將被標記為「失敗」。

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 現在返回一個 Promise,所以您必須 await 它
  await expect(buyApples()).rejects.toThrow('no id');
});

WARNING

如果斷言未被 await,那麼您將得到一個假陽性測試,每次都會通過。為了確保斷言確實被呼叫,您可以使用 expect.assertions(number)。

從 Vitest 3 開始,如果方法未被 await,Vitest 將在測試結束時顯示警告。在 Vitest 4 中,如果斷言未被 await,測試將被標記為「失敗」。

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

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

// 從 db 選擇後,我們呼叫所有回呼
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();
  });
  // 如果沒有 await,測試將失敗
  // 如果您沒有 expect.hasAssertions(),測試將通過
  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:
        // 窮盡所有錯誤測試
        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 ​

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

expect.closeTo 在比較物件屬性或陣列元素中的浮點數時很有用。如果您需要比較數字,請改用 .toBeCloseTo。

可選的 precision 參數限制了檢查小數點之後的位數。對於預設值 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 { 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 與此匹配器一起使用,以使預期值為假。

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 與此匹配器一起使用,以使預期值為假。

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 與此匹配器一起使用,以使預期值為假。

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 與此匹配器一起使用,以使預期值為假。

expect.addSnapshotSerializer ​

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

此方法新增自訂序列化器,這些序列化器在建立快照時會被呼叫。這是一個進階功能 - 如果您想了解更多,請閱讀自訂序列化器指南。

如果您要新增自訂序列化器,您應該在 setupFiles 內部呼叫此方法。這將影響所有快照。

TIP

如果您之前使用 Vue CLI 和 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() });
});

TIP

如果您希望您的匹配器出現在所有測試中,您應該在 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 {}
}

WARNING

不要忘記在您的 tsconfig.json 中包含環境宣告檔案。

TIP

如果您想了解更多,請查看擴展匹配器指南。

expect.addEqualityTesters ​

  • 類型: (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 = isAnagramComparator(b);

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

expect.addEqualityTesters([areAnagramsEqual]);

test('custom equality tester', () => {
  expect(new AnagramComparator('listen')).toEqual(
    new AnagramComparator('silent')
  );
});
Pager
上一頁Vi
下一頁expectTypeOf

以 MIT 授權條款 發布。

版權所有 (c) 2021-Present Vitest Team

https://vitest.dev/api/expect

以 MIT 授權條款 發布。

版權所有 (c) 2021-Present Vitest Team