Skip to content

Vitest 1.0:前端測試的下一代標準

Vitest 1.0 正式釋出了。從 0.x 到 1.0,它已經從"有趣的實驗"變成了可以認真用在生產環境的測試框架。

為什麼從 Jest 遷移

我們專案的痛點:

  1. ESM 支援差:Jest 對 ESM 的支援一直半生不熟,需要各種 transform 配置
  2. 配置繁瑣jest.config.js 裡一堆 moduleNameMappertransform 規則
  3. 速度慢:大型專案跑完測試要 2-3 分鐘
  4. 和 Vite 配置割裂:Vite 有一套 alias 和外掛體系,Jest 不認

Vitest 直接複用 Vite 的配置和外掛系統,天然支援 ESM、TypeScript、CSS Modules。

基礎配置

typescript
// vite.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    environment: "jsdom",
    setupFiles: ["./src/test/setup.ts"],
    coverage: {
      provider: "v8",
      reporter: ["text", "html", "lcov"],
      include: ["src/**/*.{ts,tsx}"],
      exclude: ["src/**/*.d.ts", "src/**/*.test.{ts,tsx}"],
    },
  },
});

對比 Jest 配置,精簡了很多。不需要 moduleNameMapper,因為直接用 Vite 的 resolve.alias

寫測試

typescript
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { Counter } from "./Counter";

describe("Counter", () => {
  it("點選按鈕後計數增加", async () => {
    render(<Counter />);
    const button = screen.getByRole("button");

    await fireEvent.click(button);
    await fireEvent.click(button);

    expect(screen.getByText("2")).toBeInTheDocument();
  });

  it("呼叫 onChange 回撥", () => {
    const onChange = vi.fn();
    render(<Counter onChange={onChange} />);

    fireEvent.click(screen.getByRole("button"));

    expect(onChange).toHaveBeenCalledWith(1);
    expect(onChange).toHaveBeenCalledTimes(1);
  });
});

vi.fn() 替代 jest.fn()vi.mock() 替代 jest.mock(),API 幾乎一樣,遷移成本很低。

獨特優勢

In-Source Testing

typescript
// utils.ts
export function add(a: number, b: number) {
  return a + b;
}

if (import.meta.vitest) {
  const { it, expect } = import.meta.vitest;

  it("add", () => {
    expect(add(1, 2)).toBe(3);
  });
}

測試寫在原始碼裡,對於工具函式特別方便。import.meta.vitest 在生產構建時會被 tree-shake 掉。

UI 模式

bash
vitest --ui

Vitest 自帶 Web UI,可以視覺化檢視測試結果、覆蓋率,不用額外裝外掛。

快照測試

typescript
it("渲染一致性", () => {
  const { container } = render(<UserCard user={mockUser} />);
  expect(container).toMatchSnapshot();
});

快照格式和 Jest 一致,現有快照檔案可以直接複用。

效能對比

在我們的專案上(~800 個測試用例):

Jest:    68s
Vitest:  12s (單執行緒)
Vitest:  5s  (多執行緒,預設開啟)

Vitest 預設使用 worker threads 並行執行測試,速度提升顯著。

遷移建議

bash
# 1. 安裝
pnpm add -D vitest @vitest/coverage-v8 @vitest/ui

# 2. 全域性替換 API
# jest.fn()    -> vi.fn()
# jest.mock()  -> vi.mock()
# jest.spyOn() -> vi.spyOn()
# jest.useFakeTimers() -> vi.useFakeTimers()

# 3. 刪除 jest 相關依賴
# jest, ts-jest, @types/jest, babel-jest, jest-environment-jsdom

大部分專案的遷移可以在一兩天內完成。Jest 的 matcher API(toBetoEqual 等)完全一致。

小結

  • Vitest 1.0 穩定可用,效能比 Jest 快 5-10 倍
  • 複用 Vite 配置,消除了 Jest+Vite 配置割裂的問題
  • ESM、TypeScript 原生支援,不需要額外 transform
  • 從 Jest 遷移成本很低,API 高度相容
  • In-Source Testing 和 UI 模式是額外亮點

MIT Licensed