Skip to content

Bun 的測試運行器與現有的組件和 DOM 測試庫(包括 React Testing Library 和 happy-dom)配合良好。

happy-dom

對於為前端代碼和組件編寫無頭測試,我們推薦 happy-dom。Happy DOM 在純 JavaScript 中實現了一整套 HTML 和 DOM API,使其能夠以高保真度模擬瀏覽器環境。

要開始使用,請將 @happy-dom/global-registrator 包作為開發依賴安裝。

bash
bun add -d @happy-dom/global-registrator

我們將使用 Bun 的預加載功能在運行測試之前注冊 happy-dom 全局變量。此步驟將使 document 等瀏覽器 API 在全局范圍內可用。在項目根目錄中創建一個名為 happydom.ts 的文件,並添加以下代碼:

ts
import { GlobalRegistrator } from "@happy-dom/global-registrator";

GlobalRegistrator.register();

要在 bun test 之前預加載此文件,請打開或創建 bunfig.toml 文件並添加以下行。

toml
[test]
preload = ["./happydom.ts"]

這將在你運行 bun test 時執行 happydom.ts。現在你可以編寫使用 documentwindow 等瀏覽器 API 的測試。

ts
import { test, expect } from "bun:test";

test("dom 測試", () => {
  document.body.innerHTML = `<button>My button</button>`;
  const button = document.querySelector("button");
  expect(button?.innerText).toEqual("My button");
});

TypeScript 支持

根據你的 tsconfig.json 設置,你可能會在上面的代碼中看到 "Cannot find name 'document'" 類型錯誤。要為 document 和其他瀏覽器 API "注入" 類型,請在任何測試文件的頂部添加以下三斜槓指令。

ts
/// <reference lib="dom" />

import { test, expect } from "bun:test";

test("dom 測試", () => {
  document.body.innerHTML = `<button>My button</button>`;
  const button = document.querySelector("button");
  expect(button?.innerText).toEqual("My button");
});

讓我們使用 bun test 運行此測試:

bash
bun test
bun test v1.3.3

dom.test.ts:
✓ dom test [0.82ms]

 1 pass
 0 fail
 1 expect() calls
Ran 1 tests across 1 files. 1 total [125.00ms]

React Testing Library

Bun 與 React Testing Library 無縫配合以測試 React 組件。按照上述設置 happy-dom 後,你可以正常安裝和使用 React Testing Library。

bash
bun add -d @testing-library/react @testing-library/jest-dom
ts
/// <reference lib="dom" />

import { test, expect } from 'bun:test';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';

function Button({ children }: { children: React.ReactNode }) {
  return <button>{children}</button>;
}

test('renders button', () => {
  render(<Button>Click me</Button>);
  expect(screen.getByRole('button')).toHaveTextContent('Click me');
});

高級 DOM 測試

自定義元素

你可以使用相同的設置測試自定義元素和 Web 組件:

ts
/// <reference lib="dom" />

import { test, expect } from "bun:test";

test("自定義元素", () => {
  // 定義一個自定義元素
  class MyElement extends HTMLElement {
    constructor() {
      super();
      this.innerHTML = "<p>Custom element content</p>";
    }
  }

  customElements.define("my-element", MyElement);

  // 在測試中使用它
  document.body.innerHTML = "<my-element></my-element>";
  const element = document.querySelector("my-element");
  expect(element?.innerHTML).toBe("<p>Custom element content</p>");
});

事件測試

測試 DOM 事件和用戶交互:

ts
/// <reference lib="dom" />

import { test, expect } from "bun:test";

test("按鈕點擊事件", () => {
  let clicked = false;

  document.body.innerHTML = '<button id="test-btn">Click me</button>';
  const button = document.getElementById("test-btn");

  button?.addEventListener("click", () => {
    clicked = true;
  });

  button?.click();
  expect(clicked).toBe(true);
});

配置提示

全局設置

對於更復雜的 DOM 測試設置,你可以創建更全面的預加載文件:

ts
import { GlobalRegistrator } from "@happy-dom/global-registrator";
import "@testing-library/jest-dom";

// 注冊 happy-dom 全局變量
GlobalRegistrator.register();

// 在此處添加任何全局測試配置
global.ResizeObserver = class ResizeObserver {
  observe() {}
  unobserve() {}
  disconnect() {}
};

// 模擬其他 API
Object.defineProperty(window, "matchMedia", {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

然後更新你的 bunfig.toml

toml
[test]
preload = ["./test-setup.ts"]

故障排除

常見問題

DOM API 的 TypeScript 錯誤:確保在測試文件的頂部包含 /// <reference lib="dom" /> 指令。

缺少全局變量:確保在你的預加載文件中正確導入和注冊了 @happy-dom/global-registrator

React 組件渲染問題:確保你已安裝 @testing-library/react 並正確設置了 happy-dom。

性能考慮

Happy-dom 速度很快,但對於非常大的測試套件,你可能想要:

  • 使用 beforeEach 在測試之間重置 DOM 狀態
  • 避免在單個測試中創建太多 DOM 元素
  • 考慮使用測試庫中的 cleanup 函數
ts
import { afterEach } from "bun:test";
import { cleanup } from "@testing-library/react";

afterEach(() => {
  cleanup();
  document.body.innerHTML = "";
});

Bun學習網由www.bunjs.com.cn整理維護