bun test 与 Bun 的运行时深度集成。这是使 bun test 快速且易于使用的一部分原因。
环境变量
NODE_ENV
bun test 自动将 $NODE_ENV 设置为 "test",除非它已在环境中或通过 .env 文件设置。这是大多数测试运行器的标准行为,有助于确保一致的测试行为。
import { test, expect } from "bun:test";
test("NODE_ENV 设置为 test", () => {
expect(process.env.NODE_ENV).toBe("test");
});你可以通过显式设置 NODE_ENV 来覆盖此设置:
NODE_ENV=development bun testTZ(时区)
默认情况下,所有 bun test 运行都使用 UTC(Etc/UTC)作为时区,除非被 TZ 环境变量覆盖。这确保了不同开发环境中的日期和时间行为一致。
import { test, expect } from "bun:test";
test("时区默认为 UTC", () => {
const date = new Date();
expect(date.getTimezoneOffset()).toBe(0);
});要使用特定时区进行测试:
TZ=America/New_York bun test测试超时
如果没有显式覆盖,每个测试的默认超时时间为 5000 毫秒(5 秒)。超过此超时的测试将失败。
全局超时
使用 --timeout 标志全局更改超时:
bun test --timeout 10000 # 10 秒每个测试的超时
将超时设置为测试函数的第三个参数:
import { test, expect } from "bun:test";
test("快速测试", () => {
expect(1 + 1).toBe(2);
}, 1000); // 1 秒超时
test("慢速测试", async () => {
await new Promise(resolve => setTimeout(resolve, 8000));
}, 10000); // 10 秒超时无限超时
使用 0 或 Infinity 禁用超时:
test("无超时测试", async () => {
// 此测试可以无限期运行
await someVeryLongOperation();
}, 0);错误处理
未处理的错误
bun test 跟踪未处理的 promise 拒绝和测试之间发生的错误。如果发生此类错误,最终退出码将为非零(具体为这些错误的数量),即使所有测试都通过。
这有助于捕获异步代码中否则可能被忽视的错误:
import { test } from "bun:test";
test("测试 1", () => {
// 此测试通过
expect(true).toBe(true);
});
// 此错误发生在任何测试之外
setTimeout(() => {
throw new Error("未处理的错误");
}, 0);
test("测试 2", () => {
// 此测试也通过
expect(true).toBe(true);
});
# 测试运行仍将失败,退出码为非零
# 因为未处理的错误Promise 拒绝
未处理的 promise 拒绝也会被捕获:
import { test } from "bun:test";
test("通过的测试", () => {
expect(1).toBe(1);
});
// 这将导致测试运行失败
Promise.reject(new Error("未处理的拒绝"));自定义错误处理
你可以在测试设置中设置自定义错误处理程序:
process.on("uncaughtException", error => {
console.error("未捕获的异常:", error);
process.exit(1);
});
process.on("unhandledRejection", (reason, promise) => {
console.error("未处理的拒绝:", promise, "原因:", reason);
process.exit(1);
});CLI 标志集成
几个 Bun CLI 标志可以与 bun test 一起使用以修改其行为:
内存使用
# 减少测试运行器 VM 的内存使用
bun test --smol调试
# 将调试器附加到测试运行器进程
bun test --inspect
bun test --inspect-brk模块加载
# 在测试文件之前运行脚本(用于全局设置/模拟)
bun test --preload ./setup.ts
# 设置编译时常量
bun test --define "process.env.API_URL='http://localhost:3000'"
# 配置自定义加载器
bun test --loader .special:special-loader
# 使用不同的 tsconfig
bun test --tsconfig-override ./test-tsconfig.json
# 为模块解析设置 package.json 条件
bun test --conditions development
# 为测试加载环境变量
bun test --env-file .env.test安装相关的标志
# 影响测试执行期间的任何网络请求或自动安装
bun test --prefer-offline
bun test --frozen-lockfile监视和热重载
监视模式
使用 --watch 标志运行 bun test 时,测试运行器将监视文件更改并重新运行受影响的测试。
bun test --watch测试运行器很智能地知道要重新运行哪些测试:
import { add } from "./math.js";
import { test, expect } from "bun:test";
test("加法", () => {
expect(add(2, 3)).toBe(5);
});如果你修改 math.js,只有 math.test.ts 将重新运行,而不是所有测试。
热重载
--hot 标志提供类似的功能,但在尝试在运行之间保留状态方面更积极:
bun test --hot对于大多数测试场景,--watch 是推荐的选项,因为它在测试运行之间提供更好的隔离。
全局变量
以下全局变量在测试文件中自动可用,无需导入(但如果愿意,也可以从 bun:test 导入):
// 所有这些都可以在全局范围内使用
test("全局测试函数", () => {
expect(true).toBe(true);
});
describe("全局 describe", () => {
beforeAll(() => {
// 全局 beforeAll
});
it("全局 it 函数", () => {
// it 是 test 的别名
});
});
// Jest 兼容性
jest.fn();
// Vitest 兼容性
vi.fn();如果你愿意,也可以显式导入它们:
import { test, it, describe, expect, beforeAll, beforeEach, afterAll, afterEach, jest, vi } from "bun:test";进程集成
退出码
bun test 使用标准退出码:
0:所有测试通过,没有未处理的错误1:测试失败>1:未处理错误的数量(即使测试通过)
信号处理
测试运行器正确处理常见信号:
# 优雅地停止测试执行
kill -SIGTERM <test-process-pid>
# 立即停止测试执行
kill -SIGKILL <test-process-pid>环境检测
Bun 自动检测某些环境并调整行为:
// GitHub Actions 检测
if (process.env.GITHUB_ACTIONS) {
// Bun 自动发出 GitHub Actions 注解
}
// CI 检测
if (process.env.CI) {
// 某些行为可能会针对 CI 环境进行调整
}性能考虑
单进程
测试运行器默认在单个进程中运行所有测试。这提供:
- 更快的启动 - 无需生成多个进程
- 共享内存 - 高效的资源使用
- 简单的调试 - 所有测试在一个进程中
然而,这意味着:
- 测试共享全局状态(使用生命周期钩子清理)
- 一个测试崩溃可能影响其他测试
- 没有真正的单个测试并行化
内存管理
# 监控内存使用
bun test --smol # 减少内存占用
# 对于大型测试套件,考虑拆分文件
bun test src/unit/
bun test src/integration/测试隔离
由于测试在同一进程中运行,请确保正确清理:
import { afterEach } from "bun:test";
afterEach(() => {
// 清理全局状态
global.myGlobalVar = undefined;
delete process.env.TEST_VAR;
// 如有需要重置模块
jest.resetModules();
});