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

與其他測試執行器的比較

本頁導覽

模擬(Mocking) ​

在撰寫測試時,您遲早會需要建立內部或外部服務的「虛擬」版本,這通常稱為 模擬(mocking)。Vitest 透過其 vi 輔助工具提供實用函數來協助您。您可以從 vitest 匯入該工具,如果啟用 global 配置,也可以全域存取它。

WARNING

請務必在每次測試執行前後清除或還原模擬,以確保測試之間的狀態隔離!如需更多資訊,請參閱 mockReset 文件。

如果您不熟悉 vi.fn、vi.mock 或 vi.spyOn 方法,請先參考 API 部分。

日期 ​

有時您需要控制日期以確保測試時的一致性。Vitest 使用 @sinonjs/fake-timers 套件來操作計時器和系統日期。您可於此處查閱有關特定 API 的更多詳細資訊。

範例 ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const businessHours = [9, 17];

function purchase() {
  const currentHour = new Date().getHours();
  const [open, close] = businessHours;

  if (currentHour > open && currentHour < close) {
    return { message: 'Success' };
  }

  return { message: 'Error' };
}

describe('purchasing flow', () => {
  beforeEach(() => {
    // 通知 Vitest 我們使用模擬時間
    vi.useFakeTimers();
  });

  afterEach(() => {
    // 在每次測試執行後還原日期
    vi.useRealTimers();
  });

  it('allows purchases within business hours', () => {
    // 設定營業時間內的時段
    const date = new Date(2000, 1, 1, 13);
    vi.setSystemTime(date);

    // 存取 Date.now() 將回傳上述設定的日期
    expect(purchase()).toEqual({ message: 'Success' });
  });

  it('disallows purchases outside of business hours', () => {
    // 設定營業時間外的小時
    const date = new Date(2000, 1, 1, 19);
    vi.setSystemTime(date);

    // 存取 Date.now() 將回傳上述設定的日期
    expect(purchase()).toEqual({ message: 'Error' });
  });
});

函數 ​

模擬函數可以分為兩種類別:監控(spying)和模擬(mocking)。

有時您只需要驗證特定函數是否已被呼叫(以及可能傳遞了哪些參數)。在這些情況下,監控就足夠了,您可以直接使用 vi.spyOn() (詳情請參閱)。

然而,監控只能幫助您監控函數,它們無法更改這些函數的實作。在我們確實需要建立函數的模擬版本的情況下,我們可以使用 vi.fn() (詳情請參閱)。

我們使用 Tinyspy 作為模擬函數的基礎,但我們有自己的封裝器使其與 jest 相容。vi.fn() 和 vi.spyOn() 都共享相同的方法,但只有 vi.fn() 的回傳結果可供呼叫。

範例 ​

js
import { afterEach, describe, expect, it, vi } from 'vitest';

const messages = {
  items: [
    { message: 'Simple test message', from: 'Testman' },
    // ...
  ],
  getLatest, // 若環境支援,亦可作為 `getter` 或 `setter`
};

function getLatest(index = messages.items.length - 1) {
  return messages.items[index];
}

describe('reading messages', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should get the latest message with a spy', () => {
    const spy = vi.spyOn(messages, 'getLatest');
    expect(spy.getMockName()).toEqual('getLatest');

    expect(messages.getLatest()).toEqual(
      messages.items[messages.items.length - 1]
    );

    expect(spy).toHaveBeenCalledTimes(1);

    spy.mockImplementationOnce(() => 'access-restricted');
    expect(messages.getLatest()).toEqual('access-restricted');

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

  it('should get with a mock', () => {
    const mock = vi.fn().mockImplementation(getLatest);

    expect(mock()).toEqual(messages.items[messages.items.length - 1]);
    expect(mock).toHaveBeenCalledTimes(1);

    mock.mockImplementationOnce(() => 'access-restricted');
    expect(mock()).toEqual('access-restricted');

    expect(mock).toHaveBeenCalledTimes(2);

    expect(mock()).toEqual(messages.items[messages.items.length - 1]);
    expect(mock).toHaveBeenCalledTimes(3);
  });
});

更多 ​

  • Jest 的模擬函數

全域變數 ​

您可以使用 vi.stubGlobal 輔助工具來模擬 jsdom 或 node 中不存在的全域變數。它會將全域變數的值放入 globalThis 物件中。

ts
import { vi } from 'vitest';

const IntersectionObserverMock = vi.fn(() => ({
  disconnect: vi.fn(),
  observe: vi.fn(),
  takeRecords: vi.fn(),
  unobserve: vi.fn(),
}));

vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);

// 現在您可以將其作為 `IntersectionObserver` 或 `window.IntersectionObserver` 存取

模組 ​

模擬模組可監控其他程式碼所呼叫的第三方函式庫,允許您測試參數、輸出,甚至重新宣告其實現。

有關更深入的詳細 API 描述,請參閱 vi.mock() API 部分。

自動模擬演算法 ​

如果您的程式碼正在匯入一個被模擬的模組,而該模組沒有任何相關的 __mocks__ 檔案或 factory,Vitest 將透過呼叫它並模擬每個匯出來模擬該模組本身。

遵循以下原則:

  • 所有陣列都將被清空
  • 所有基本類型和集合都將保持不變
  • 所有物件都將被深度複製
  • 所有類別的實例及其原型都將被深度複製

虛擬模組 ​

Vitest 支援模擬 Vite 虛擬模組。它的工作方式與 Jest 中處理虛擬模組的方式不同。您不需要將 virtual: true 傳遞給 vi.mock 函數,而是需要告訴 Vite 該模組存在,否則它會在解析時失敗。您可以透過幾種方式做到這一點:

  1. 提供別名
ts
import { defineConfig } from 'vitest/config';
import { resolve } from 'node:path';
export default defineConfig({
  test: {
    alias: {
      '$app/forms': resolve('./mocks/forms.js'),
    },
  },
});
  1. 提供一個解析虛擬模組的外掛程式
ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
  plugins: [
    {
      name: 'virtual-modules',
      resolveId(id) {
        if (id === '$app/forms') {
          return 'virtual:$app/forms';
        }
      },
    },
  ],
});

第二種方法的優點是您可以動態建立不同的虛擬入口點。如果您將多個虛擬模組重新導向到單一檔案,那麼所有這些模組都將受到 vi.mock 的影響,因此請務必使用唯一的識別符號。

模擬陷阱 ​

請注意,不可能模擬在同一檔案內部呼叫的其他方法。例如,在此程式碼中:

ts
export function foo() {
  return 'foo';
}

export function foobar() {
  return `${foo()}bar`;
}

不可能從外部模擬 foo 方法,因為它是直接引用的。因此,此程式碼對 foobar 內部呼叫 foo 沒有影響(但它會影響其他模組中呼叫 foo 的情況):

ts
import { vi } from 'vitest';
import * as mod from './foobar.js';

// 這只會影響原始模組外部的 "foo"
vi.spyOn(mod, 'foo');
vi.mock('./foobar.js', async importOriginal => {
  return {
    ...(await importOriginal<typeof import('./foobar.js')>()),
    // 這只會影響原始模組外部的 "foo"
    foo: () => 'mocked',
  };
});

您可以透過直接向 foobar 方法提供實作方式來確認此行為:

ts
import * as mod from './foobar.js';

vi.spyOn(mod, 'foo');

// 匯出的 foo 引用模擬方法
mod.foobar(mod.foo);
ts
export function foo() {
  return 'foo';
}

export function foobar(injectedFoo) {
  return injectedFoo === foo; // false
}

這是預期的行為。當模擬以這種方式使用時,通常是程式碼不良的跡象。考慮將您的程式碼重構為多個檔案,或透過使用 依賴注入 等技術來改進您的應用程式架構。

範例 ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Client } from 'pg';
import { failure, success } from './handlers.js';

// 取得待辦事項
export async function getTodos(event, context) {
  const client = new Client({
    // ...clientOptions
  });

  await client.connect();

  try {
    const result = await client.query('SELECT * FROM todos;');

    client.end();

    return success({
      message: `${result.rowCount} item(s) returned`,
      data: result.rows,
      status: true,
    });
  } catch (e) {
      console.error(e.stack);

    client.end();

    return failure({ message: e, status: false });
  }
}

vi.mock('pg', () => {
  const Client = vi.fn();
  Client.prototype.connect = vi.fn();
  Client.prototype.query = vi.fn();
  Client.prototype.end = vi.fn();

  return { Client };
});

vi.mock('./handlers.js', () => {
  return {
    success: vi.fn(),
    failure: vi.fn(),
  };
});

describe('get a list of todo items', () => {
  let client;

  beforeEach(() => {
    client = new Client();
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  it('should return items successfully', async () => {
    client.query.mockResolvedValueOnce({ rows: [], rowCount: 0 });

    await getTodos();

    expect(client.connect).toBeCalledTimes(1);
    expect(client.query).toBeCalledWith('SELECT * FROM todos;');
    expect(client.end).toBeCalledTimes(1);

    expect(success).toBeCalledWith({
      message: '0 item(s) returned',
      data: [],
      status: true,
    });
  });

  it('should throw an error', async () => {
    const mError = new Error('Unable to retrieve rows');
    client.query.mockRejectedValueOnce(mError);

    await getTodos();

    expect(client.connect).toBeCalledTimes(1);
    expect(client.query).toBeCalledWith('SELECT * FROM todos;');
    expect(client.end).toBeCalledTimes(1);
    expect(failure).toBeCalledWith({ message: mError, status: false });
  });
});

檔案系統 ​

模擬檔案系統可確保測試不依賴實際檔案系統,從而使測試更可靠和可預測。這種隔離有助於避免先前測試所造成的副作用。它允許測試錯誤條件和邊緣情況,這些情況可能難以或不可能透過實際檔案系統重現,例如權限問題、磁碟空間不足或讀/寫錯誤。

Vitest 不提供任何內建的檔案系統模擬 API。您可以使用 vi.mock 手動模擬 fs 模組,但很難維護。相反,我們建議使用 memfs 為您完成此操作。memfs 建立一個記憶體檔案系統,它模擬檔案系統操作而不會觸及實際磁碟。這種方法快速且安全,避免對實際檔案系統產生任何潛在副作用。

範例 ​

若要自動將每個 fs 呼叫重新導向到 memfs,您可以在專案根目錄建立 __mocks__/fs.cjs 和 __mocks__/fs/promises.cjs 檔案:

ts
// 我們也可以使用 `import`,但這樣
// 每個匯出都應該明確定義

const { fs } = require('memfs');
module.exports = fs;
ts
// 我們也可以使用 `import`,但這樣
// 每個匯出都應該明確定義

const { fs } = require('memfs');
module.exports = fs.promises;
ts
import { readFileSync } from 'node:fs';

export function readHelloWorld(path) {
  return readFileSync(path, 'utf-8');
}
ts
import { beforeEach, expect, it, vi } from 'vitest';
import { fs, vol } from 'memfs';
import { readHelloWorld } from './read-hello-world.js';

// 通知 Vitest 使用 __mocks__ 資料夾中的 fs 模擬
// 如果 fs 應該始終被模擬,這可以在設定檔中完成
vi.mock('node:fs');
vi.mock('node:fs/promises');

beforeEach(() => {
  // 重置記憶體內 fs 的狀態
  vol.reset();
});

it('should return correct text', () => {
  const path = '/hello-world.txt';
  fs.writeFileSync(path, 'hello world');

  const text = readHelloWorld(path);
  expect(text).toBe('hello world');
});

it('can return a value multiple times', () => {
  // 您可以使用 vol.fromJSON 來定義多個檔案
  vol.fromJSON(
    {
      './dir1/hw.txt': 'hello dir1',
      './dir2/hw.txt': 'hello dir2',
    },
    // 預設 cwd
    '/tmp'
  );

  expect(readHelloWorld('/tmp/dir1/hw.txt')).toBe('hello dir1');
  expect(readHelloWorld('/tmp/dir2/hw.txt')).toBe('hello dir2');
});

請求 ​

由於 Vitest 在 Node 中執行,模擬網路請求較為複雜;Web API 不可用,因此我們需要一些東西來為我們模擬網路行為。我們建議使用 Mock Service Worker 來實現此目的。它允許您模擬 http、WebSocket 和 GraphQL 網路請求,並且與框架無關。

Mock Service Worker (MSW) 的工作原理是攔截您的測試發出的請求,讓您無需修改應用程式程式碼即可使用。在瀏覽器中,此功能使用 Service Worker API。在 Node.js 中,對於 Vitest,它使用 @mswjs/interceptors 函式庫。要了解有關 MSW 的更多資訊,請閱讀其 介紹

配置 ​

您可以在 設定檔 中如下使用它

js
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';

const posts = [
  {
    userId: 1,
    id: 1,
    title: 'first post title',
    body: 'first post body',
  },
  // ...
];

export const restHandlers = [
  http.get('https://rest-endpoint.example/path/to/posts', () => {
    return HttpResponse.json(posts);
  }),
];

const server = setupServer(...restHandlers);

// 在所有測試之前啟動服務
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// 在所有測試之後關閉服務
afterAll(() => server.close());

// 於每個測試後重置處理函式以確保測試隔離
afterEach(() => server.resetHandlers());
js
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { graphql, HttpResponse } from 'msw';

const posts = [
  {
    userId: 1,
    id: 1,
    title: 'first post title',
    body: 'first post body',
  },
  // ...
];

const graphqlHandlers = [
  graphql.query('ListPosts', () => {
    return HttpResponse.json({
      data: { posts },
    });
  }),
];

const server = setupServer(...graphqlHandlers);

// 在所有測試之前啟動服務
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// 在所有測試之後關閉服務
afterAll(() => server.close());

// 於每個測試後重置處理函式以確保測試隔離
afterEach(() => server.resetHandlers());
js
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { ws } from 'msw';

const chat = ws.link('wss://chat.example.com');

const wsHandlers = [
  chat.addEventListener('connection', ({ client }) => {
    client.addEventListener('message', event => {
      console.log('Received message from client:', event.data);
      // 將收到的訊息回傳給客戶端
      client.send(`Server received: ${event.data}`);
    });
  }),
];

const server = setupServer(...wsHandlers);

// 在所有測試之前啟動服務
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// 在所有測試之後關閉服務
afterAll(() => server.close());

// 於每個測試後重置處理函式以確保測試隔離
afterEach(() => server.resetHandlers());

使用 onUnhandledRequest: 'error' 配置服務可確保在有請求沒有對應的請求處理器時拋出錯誤。

更多 ​

MSW 還有更多功能。您可存取 cookie 與查詢參數,定義模擬錯誤回應等等!要查看 MSW 的所有功能,請閱讀 其文件。

計時器 ​

當我們測試涉及逾時或間隔的程式碼時,我們可以透過使用模擬計時器來模擬對 setTimeout 和 setInterval 的呼叫,從而加快測試速度,而不是讓測試等待或逾時。

有關更深入的詳細 API 描述,請參閱 vi.useFakeTimers API 部分。

範例 ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

function executeAfterTwoHours(func) {
  setTimeout(func, 1000 * 60 * 60 * 2); // 2 小時
}

function executeEveryMinute(func) {
  setInterval(func, 1000 * 60); // 1 分鐘
}

const mock = vi.fn(() => console.log('executed'));

describe('delayed execution', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });
  afterEach(() => {
    vi.restoreAllMocks();
  });
  it('should execute the function', () => {
    executeAfterTwoHours(mock);
    vi.runAllTimers();
    expect(mock).toHaveBeenCalledTimes(1);
  });
  it('should not execute the function', () => {
    executeAfterTwoHours(mock);
    // 提前 2 毫秒不會觸發函數
    vi.advanceTimersByTime(2);
    expect(mock).not.toHaveBeenCalled();
  });
  it('should execute every minute', () => {
    executeEveryMinute(mock);
    vi.advanceTimersToNextTimer();
    expect(mock).toHaveBeenCalledTimes(1);
    vi.advanceTimersToNextTimer();
    expect(mock).toHaveBeenCalledTimes(2);
  });
});

類別 ​

您可以透過單一 vi.fn 呼叫來模擬整個類別 - 因為所有類別也都是函數,所以這內建支援。請注意,目前 Vitest 不支援 new 關鍵字,因此函數主體中的 new.target 始終為 undefined。

ts
class Dog {
  name: string;

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

  static getType(): string {
    return 'animal';
  }

  greet = (): string => {
    return `Hi! My name is ${this.name}!`;
  };

  speak(): string {
    return 'bark!';
  }

  isHungry() {}
  feed() {}
}

我們可以使用 ES5 函數重新建立這個類別:

ts
const Dog = vi.fn(function (name) {
  this.name = name;
  // 在建構函式中模擬實例方法,每個實例都將有自己的監聽器
  this.greet = vi.fn(() => `Hi! My name is ${this.name}!`);
});

// 請注意,靜態方法直接在函數上模擬,
// 而不是在類別的實例上
Dog.getType = vi.fn(() => 'mocked animal');

// 模擬類別的每個實例上的「speak」和「feed」方法
// 所有 `new Dog()` 實例都將繼承並共享這些監聽器
Dog.prototype.speak = vi.fn(() => 'loud bark!');
Dog.prototype.feed = vi.fn();

WARNING

如果建構函式返回非原始值,則該值將成為新表達式的結果。在這種情況下,[[Prototype]] 可能無法正確綁定:

ts
const CorrectDogClass = vi.fn(function (name) {
  this.name = name;
});

const IncorrectDogClass = vi.fn(name => ({
  name,
}));

const Marti = new CorrectDogClass('Marti');
const Newt = new IncorrectDogClass('Newt');

Marti instanceof CorrectDogClass; // ✅ true
Newt instanceof IncorrectDogClass; // ❌ false!

何時使用?

一般來說,如果類別從另一個模組重新匯出,您會在模組工廠內部重新定義類別,如下所示:

ts
import { Dog } from './dog.js';

vi.mock(import('./dog.js'), () => {
  const Dog = vi.fn();
  Dog.prototype.feed = vi.fn();
  // ... 其他模擬
  return { Dog };
});

此方法還可以用於將類別實例傳遞給接受相同介面定義的函數:

ts
function feed(dog: Dog) {
  // ...
}
ts
import { expect, test, vi } from 'vitest';
import { feed } from '../src/feed.js';

const Dog = vi.fn();
Dog.prototype.feed = vi.fn();

test('can feed dogs', () => {
  const dogMax = new Dog('Max');

  feed(dogMax);

  expect(dogMax.feed).toHaveBeenCalled();
  expect(dogMax.isHungry()).toBe(false);
});

現在,當我們建立 Dog 類別的新實例時,它的 speak 方法(以及 feed 和 greet)已經被模擬了:

ts
const Cooper = new Dog('Cooper');
Cooper.speak(); // loud bark!
Cooper.greet(); // Hi! My name is Cooper!

// 您可以使用內建斷言來檢查呼叫的有效性
expect(Cooper.speak).toHaveBeenCalled();
expect(Cooper.greet).toHaveBeenCalled();

const Max = new Dog('Max');

// 分配給原型的方​​法在實例之間共享
expect(Max.speak).toHaveBeenCalled();
expect(Max.greet).not.toHaveBeenCalled();

我們可以為特定實例重新分配回傳值:

ts
const dog = new Dog('Cooper');

// "vi.mocked" 是一個類型輔助工具,因為
// TypeScript 不知道 Dog 是一個模擬類別,
// 它將任何函數包裝在 MockInstance<T> 類型中,且不驗證該函數是否為模擬
vi.mocked(dog.speak).mockReturnValue('woof woof');

dog.speak(); // woof woof

要模擬屬性,我們可以使用 vi.spyOn(dog, 'name', 'get') 方法。這使得可以在模擬屬性上使用監控斷言:

ts
const dog = new Dog('Cooper');

const nameSpy = vi.spyOn(dog, 'name', 'get').mockReturnValue('Max');

expect(dog.name).toBe('Max');
expect(nameSpy).toHaveBeenCalledTimes(1);

TIP

您也可以使用相同的方法監控 getter 和 setter。

速查表 ​

INFO

以下範例中的 vi 直接從 vitest 匯入。如果您在 config 中將 globals 設定為 true,您也可以全域使用它。

我想要…

模擬匯出變數 ​

js
export const getter = 'variable';
ts
import * as exports from './example.js';

vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked');

模擬匯出函數 ​

  1. vi.mock 範例:

WARNING

別忘了 vi.mock 呼叫會被提升(hoisted)到檔案頂部。它將始終在所有匯入之前執行。

ts
export function method() {}
ts
import { method } from './example.js';

vi.mock('./example.js', () => ({
  method: vi.fn(),
}));
  1. vi.spyOn 範例:
ts
import * as exports from './example.js';

vi.spyOn(exports, 'method').mockImplementation(() => {});

模擬匯出類別實作 ​

  1. vi.mock 和 .prototype 範例:
ts
export class SomeClass {}
ts
import { SomeClass } from './example.js';

vi.mock(import('./example.js'), () => {
  const SomeClass = vi.fn();
  SomeClass.prototype.someMethod = vi.fn();
  return { SomeClass };
});
// SomeClass.mock.instances 將會有 SomeClass
  1. vi.spyOn 範例:
ts
import * as mod from './example.js';

const SomeClass = vi.fn();
SomeClass.prototype.someMethod = vi.fn();

vi.spyOn(mod, 'SomeClass').mockImplementation(SomeClass);

監控從函數返回的物件 ​

  1. 使用快取範例:
ts
export function useObject() {
  return { method: () => true };
}
ts
import { useObject } from './example.js';

const obj = useObject();
obj.method();
ts
import { useObject } from './example.js';

vi.mock(import('./example.js'), () => {
  let _cache;
  const useObject = () => {
    if (!_cache) {
      _cache = {
        method: vi.fn(),
      };
    }
    // 現在每次呼叫 useObject() 都會
    // 返回相同的物件參考
    return _cache;
  };
  return { useObject };
});

const obj = useObject();
// obj.method 在 some-path 內部被呼叫
expect(obj.method).toHaveBeenCalled();

模擬模組的一部分 ​

ts
import { mocked, original } from './some-path.js';

vi.mock(import('./some-path.js'), async importOriginal => {
  const mod = await importOriginal();
  return {
    ...mod,
    mocked: vi.fn(),
  };
});
original(); // 具有原始行為
mocked(); // 是一個監控函數

WARNING

別忘了這只會 模擬 外部 呼叫。在此範例中,如果 original 在內部呼叫 mocked,它將始終呼叫模組中定義的函數,而不是模擬函式中的函數。

模擬當前日期 ​

若要模擬 Date 的時間,您可以使用 vi.setSystemTime 輔助函數。此值不會在不同測試之間自動重置。

請注意,使用 vi.useFakeTimers 也會更改 Date 的時間。

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// 重置模擬時間
vi.useRealTimers();

模擬全域變數 ​

您可以透過將值指派給 globalThis 或使用 vi.stubGlobal 輔助工具來設定全域變數。使用 vi.stubGlobal 時,它不會在不同測試之間自動重置,除非您啟用 unstubGlobals 配置選項或呼叫 vi.unstubAllGlobals。

ts
vi.stubGlobal('__VERSION__', '1.0.0');
expect(__VERSION__).toBe('1.0.0');

模擬 import.meta.env ​

  1. 要更改環境變數,您只需為其指派一個新值。

WARNING

環境變數值不會在不同測試之間自動重置。

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

// 在執行測試之前,"VITE_ENV" 的值為 "test"
import.meta.env.VITE_ENV === 'test';

beforeEach(() => {
  import.meta.env.VITE_ENV = originalViteEnv;
});

it('changes value', () => {
  import.meta.env.VITE_ENV = 'staging';
  expect(import.meta.env.VITE_ENV).toBe('staging');
});
  1. 如果您想自動重置值,可以使用 vi.stubEnv 輔助工具並啟用 unstubEnvs 配置選項(或在 beforeEach 鉤子中手動呼叫 vi.unstubAllEnvs):
ts
import { expect, it, vi } from 'vitest';

// 在執行測試之前,"VITE_ENV" 的值為 "test"
import.meta.env.VITE_ENV === 'test';

it('changes value', () => {
  vi.stubEnv('VITE_ENV', 'staging');
  expect(import.meta.env.VITE_ENV).toBe('staging');
});

it('the value is restored before running an other test', () => {
  expect(import.meta.env.VITE_ENV).toBe('test');
});
ts
export default defineConfig({
  test: {
    unstubEnvs: true,
  },
});
Pager
上一頁快照
下一頁平行化

以 MIT 授權條款 發布。

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

https://vitest.dev/guide/mocking

以 MIT 授權條款 發布。

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