Bun Jest Migration by secondsky/claude-skills
npx skills add https://github.com/secondsky/claude-skills --skill 'Bun Jest Migration'Bun 的测试运行器与 Jest 兼容。大多数 Jest 测试无需修改即可运行。
# 1. 移除 Jest 依赖
bun remove jest ts-jest @types/jest babel-jest
# 2. 更新测试脚本
# package.json: "test": "bun test"
# 3. 运行测试
bun test
// 之前 (Jest)
import { describe, it, expect, jest } from '@jest/globals';
// 之后 (Bun) - 无需导入,或显式导入:
import { describe, test, expect, mock, spyOn } from "bun:test";
| Jest | Bun | 说明 |
|---|---|---|
describe() | describe() | 完全相同 |
| / |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
it()test()test() |
使用 test() |
expect() | expect() | 相同的匹配器 |
beforeAll/Each | beforeAll/Each | 完全相同 |
afterAll/Each | afterAll/Each | 完全相同 |
jest.fn() | mock() | 使用 mock() |
jest.spyOn() | spyOn() | 完全相同 |
| Jest | Bun 等效方法 |
|---|---|
jest.mock('module') | mock.module('module', () => {...}) |
jest.useFakeTimers() | import { setSystemTime } from "bun:test" |
jest.setTimeout() | test() 的第三个参数 |
jest.clearAllMocks() | 在每个 mock 上调用 .mockClear() |
// Jest
const fn = jest.fn().mockReturnValue('value');
// Bun
const fn = mock(() => 'value');
// 或为了兼容性:
import { jest } from "bun:test";
const fn = jest.fn(() => 'value');
// Jest (顶层提升)
jest.mock('./utils', () => ({
helper: jest.fn(() => 'mocked')
}));
// Bun (内联,无提升)
import { mock } from "bun:test";
mock.module('./utils', () => ({
helper: mock(() => 'mocked')
}));
// Jest
jest.spyOn(console, 'log').mockImplementation(() => {});
// Bun (完全相同)
spyOn(console, 'log').mockImplementation(() => {});
// Jest
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01'));
jest.advanceTimersByTime(1000);
// Bun - 支持 Jest 兼容的定时器 API
import { setSystemTime } from "bun:test";
import { jest } from "bun:test";
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01'));
jest.advanceTimersByTime(1000); // 现已支持
// Jest
expect(component).toMatchSnapshot();
expect(data).toMatchInlineSnapshot(`"expected"`);
// Bun (完全相同)
expect(component).toMatchSnapshot();
expect(data).toMatchInlineSnapshot(`"expected"`);
更新快照:
bun test --update-snapshots
// jest.config.js (之前)
module.exports = {
testMatch: ['**/*.test.ts'],
testTimeout: 10000,
setupFilesAfterEnv: ['./jest.setup.ts'],
collectCoverage: true,
coverageThreshold: { global: { lines: 80 } }
};
# bunfig.toml (之后)
[test]
root = "./"
preload = ["./jest.setup.ts"]
timeout = 10000
coverage = true
coverageThreshold = 0.8
jest.mock 不工作// Jest 的 mock 提升在 Bun 中不存在
// 将 mock.module 移到导入之前,或使用动态导入
// 解决方案 1:在顶部使用 mock.module
mock.module('./api', () => ({ fetch: mock() }));
import { fetch } from './api';
// 解决方案 2:动态导入
const mockFetch = mock();
mock.module('./api', () => ({ fetch: mockFetch }));
const { fetch } = await import('./api');
// Bun 的定时器支持有限
// 使用 setSystemTime 进行日期模拟
import { setSystemTime } from "bun:test";
beforeEach(() => {
setSystemTime(new Date('2024-01-01'));
});
afterEach(() => {
setSystemTime(); // 重置为真实时间
});
// Jest
expect.extend({ toBeWithinRange(received, floor, ceiling) {...} });
// Bun (相同 API)
import { expect } from "bun:test";
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
return {
pass,
message: () => `expected ${received} to be within ${floor}-${ceiling}`
};
}
});
移除 Jest 包
bun remove jest ts-jest @types/jest babel-jest jest-environment-jsdom
更新 package.json
{
"scripts": {
"test": "bun test",
"test:watch": "bun test --watch",
"test:coverage": "bun test --coverage"
}
}
将 jest.config.js 转换为 bunfig.toml
更新测试文件中的导入
@jest/globals → bun:testjest.fn() → mock()jest.mock() → mock.module()运行并修复
bun test 2>&1 | head -50 # 检查前 50 个错误
| 错误 | 原因 | 修复方法 |
|---|---|---|
Cannot find module '@jest/globals' | 旧的导入 | 使用 bun:test |
jest is not defined | 全局 jest | 从 bun:test 导入 |
mock.module is not a function | 错误的导入 | import { mock } from "bun:test" |
Snapshot mismatch | 不同的序列化 | 使用 --update-snapshots 更新 |
在以下情况下加载 references/compatibility-matrix.md:
每周安装量
–
代码仓库
GitHub 星标数
90
首次出现时间
–
安全审计
Bun's test runner is Jest-compatible. Most Jest tests run without changes.
# 1. Remove Jest dependencies
bun remove jest ts-jest @types/jest babel-jest
# 2. Update test script
# package.json: "test": "bun test"
# 3. Run tests
bun test
// Before (Jest)
import { describe, it, expect, jest } from '@jest/globals';
// After (Bun) - No import needed, or explicit:
import { describe, test, expect, mock, spyOn } from "bun:test";
| Jest | Bun | Notes |
|---|---|---|
describe() | describe() | Identical |
it() / test() | test() | Use test() |
expect() | expect() | Same matchers |
beforeAll/Each | beforeAll/Each | Identical |
afterAll/Each | afterAll/Each | Identical |
jest.fn() | mock() | Use mock() |
jest.spyOn() | spyOn() | Identical |
| Jest | Bun Equivalent |
|---|---|
jest.mock('module') | mock.module('module', () => {...}) |
jest.useFakeTimers() | import { setSystemTime } from "bun:test" |
jest.setTimeout() | Third argument to test() |
jest.clearAllMocks() | Call .mockClear() on each mock |
// Jest
const fn = jest.fn().mockReturnValue('value');
// Bun
const fn = mock(() => 'value');
// Or for compatibility:
import { jest } from "bun:test";
const fn = jest.fn(() => 'value');
// Jest (top-level hoisting)
jest.mock('./utils', () => ({
helper: jest.fn(() => 'mocked')
}));
// Bun (inline, no hoisting)
import { mock } from "bun:test";
mock.module('./utils', () => ({
helper: mock(() => 'mocked')
}));
// Jest
jest.spyOn(console, 'log').mockImplementation(() => {});
// Bun (identical)
spyOn(console, 'log').mockImplementation(() => {});
// Jest
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01'));
jest.advanceTimersByTime(1000);
// Bun - supports Jest-compatible timer APIs
import { setSystemTime } from "bun:test";
import { jest } from "bun:test";
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01'));
jest.advanceTimersByTime(1000); // Now supported
// Jest
expect(component).toMatchSnapshot();
expect(data).toMatchInlineSnapshot(`"expected"`);
// Bun (identical)
expect(component).toMatchSnapshot();
expect(data).toMatchInlineSnapshot(`"expected"`);
Update snapshots:
bun test --update-snapshots
// jest.config.js (before)
module.exports = {
testMatch: ['**/*.test.ts'],
testTimeout: 10000,
setupFilesAfterEnv: ['./jest.setup.ts'],
collectCoverage: true,
coverageThreshold: { global: { lines: 80 } }
};
# bunfig.toml (after)
[test]
root = "./"
preload = ["./jest.setup.ts"]
timeout = 10000
coverage = true
coverageThreshold = 0.8
jest.mock Not Working// Jest mock hoisting doesn't exist in Bun
// Move mock.module before imports or use dynamic imports
// Solution 1: Use mock.module at top
mock.module('./api', () => ({ fetch: mock() }));
import { fetch } from './api';
// Solution 2: Dynamic import
const mockFetch = mock();
mock.module('./api', () => ({ fetch: mockFetch }));
const { fetch } = await import('./api');
// Bun timer support is limited
// Use setSystemTime for date mocking
import { setSystemTime } from "bun:test";
beforeEach(() => {
setSystemTime(new Date('2024-01-01'));
});
afterEach(() => {
setSystemTime(); // Reset to real time
});
// Jest
expect.extend({ toBeWithinRange(received, floor, ceiling) {...} });
// Bun (same API)
import { expect } from "bun:test";
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
return {
pass,
message: () => `expected ${received} to be within ${floor}-${ceiling}`
};
}
});
Remove Jest packages
bun remove jest ts-jest @types/jest babel-jest jest-environment-jsdom
Update package.json
{
"scripts": {
"test": "bun test",
"test:watch": "bun test --watch",
"test:coverage": "bun test --coverage"
}
}
Convert jest.config.js to bunfig.toml
Update imports in test files
@jest/globals → bun:testjest.fn() → mock()jest.mock() → | Error | Cause | Fix |
|---|---|---|
Cannot find module '@jest/globals' | Old import | Use bun:test |
jest is not defined | Global jest | Import from bun:test |
mock.module is not a function | Wrong import | import { mock } from "bun:test" |
Snapshot mismatch |
Load references/compatibility-matrix.md when:
Weekly Installs
–
Repository
GitHub Stars
90
First Seen
–
Security Audits
Vue 3 调试指南:解决响应式、计算属性与监听器常见错误
11,400 周安装
mock.module()Run and fix
bun test 2>&1 | head -50 # Check first errors
| Different serialization |
Update with --update-snapshots |