Documentation Index
Fetch the complete documentation index at: https://bun.zhcndoc.com/llms.txt
Use this file to discover all available pages before exploring further.
Bun 的测试运行器可以很好地配合现有的组件和 DOM 测试库使用,包括 React Testing Library 和 happy-dom。
happy-dom
对于为前端代码和组件编写无头测试,我们推荐使用 happy-dom。Happy DOM 用纯 JavaScript 实现了一整套完整的 HTML 和 DOM API,使得可以高保真地模拟浏览器环境。
开始使用前,请将 @happy-dom/global-registrator 包安装为开发依赖。
bun add -d @happy-dom/global-registrator
我们将使用 Bun 的预加载功能,在运行测试之前注册 happy-dom 的全局变量。此步骤会使得像 document 这样的浏览器 API 在全局作用域中可用。在项目根目录下创建一个名为 happydom.ts 的文件,并添加以下代码:
happydom.tsimport { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register();
要在执行 bun test 之前预加载此文件,请打开或创建 bunfig.toml 文件,并添加以下内容:
[test]
preload = ["./happydom.ts"]
这样在运行 bun test 时会执行 happydom.ts。现在你就可以编写使用浏览器 API(如 document 和 window)的测试了。
dom.test.tsimport { 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 配置,以上代码中可能会出现“无法找到名称 ‘document’”的类型错误。为“注入”document 和其他浏览器 API 的类型,请在任意测试文件顶部添加以下三斜线指令。
dom.test.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 来运行此测试:
bun test v1.3.3
dom.test.ts:
✓ dom 测试 [0.82ms]
1 通过
0 失败
1 次 expect() 调用
在 1 个文件中共运行了 1 个测试。总耗时 [125.00ms]
React Testing Library
Bun 与 React Testing Library 无缝配合,可用于测试 React 组件。按照上述方法配置好 happy-dom 后,你可以正常安装并使用 React Testing Library。
bun add -d @testing-library/react @testing-library/jest-dom
component.test.tsx/// <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('渲染按钮', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button')).toHaveTextContent('Click me');
});
高级 DOM 测试
自定义元素
你可以使用相同的配置来测试自定义元素和 Web 组件:
custom-element.test.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 事件和用户交互:
events.test.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 测试配置,你可以创建一个更完整的预加载文件:
test-setup.tsimport { 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:
[test]
preload = ["./test-setup.ts"]
故障排除
常见问题
TypeScript 对 DOM API 的错误:确保在测试文件顶部包含 /// <reference lib="dom" /> 指令。
缺失全局变量:确保 @happy-dom/global-registrator 已正确导入并在预加载文件中注册。
React 组件渲染问题:确保已安装 @testing-library/react,且 happy-dom 配置正确。
性能考虑
happy-dom 性能很快,但对于非常大的测试套件,你可能需要:
- 使用
beforeEach 在测试间重置 DOM 状态
- 避免在单个测试中创建过多 DOM 元素
- 考虑使用测试库中的
cleanup 函数
test-setup.tsimport { afterEach } from "bun:test";
import { cleanup } from "@testing-library/react";
afterEach(() => {
cleanup();
document.body.innerHTML = "";
});