Guides
Testing
Unit, integration, and E2E testing with Bun.
Testing
Bklar is built on Bun, so use the native bun:test runner. It's fast and API-compatible with Jest.
In-Memory Testing
app.request() simulates requests without starting a TCP server — perfect for unit and integration tests.
import { describe, expect, it, beforeEach } from "bun:test";
import { Bklar } from "bklar";
describe("My API", () => {
let app: ReturnType<typeof Bklar>;
beforeEach(() => {
app = Bklar({ logger: false });
app.get("/hello", (ctx) => ctx.json({ hello: "world" }));
app.post("/submit", (ctx) => ctx.body, {
schemas: { body: z.object({ name: z.string() }) },
});
});
it("should return hello world", async () => {
const res = await app.request("/hello");
expect(res.status).toBe(200);
const body = await res.json();
expect(body).toEqual({ hello: "world" });
});
it("should validate request bodies", async () => {
const res = await app.request("/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: 123 }),
});
expect(res.status).toBe(400);
});
});E2E Testing (Real Server)
For full integration tests, start a real server and use fetch():
import { describe, expect, it, afterEach } from "bun:test";
import { Bklar } from "bklar";
describe("E2E", () => {
let server: ReturnType<typeof Bklar.prototype.listen>;
afterEach(() => {
if (server) server.stop(false);
});
it("should handle real HTTP requests", async () => {
const app = Bklar({ logger: false });
app.get("/", () => ({ status: "ok" }));
server = app.listen(0); // Port 0 = random available port
const res = await fetch(`http://localhost:${server.port}/`);
expect(res.status).toBe(200);
expect(res.headers.get("X-Request-Id")).toBeTruthy();
});
});Testing Middleware Priority
it("should run middleware in priority order", async () => {
const order: string[] = [];
const app = Bklar({ logger: false });
app.use(async (_, next) => { order.push("C"); return next(); }, 10);
app.use(async (_, next) => { order.push("A"); return next(); }, -10);
app.get("/", () => { order.push("H"); return "ok"; });
await app.request("/");
expect(order).toEqual(["A", "C", "H"]);
});Testing SSE
it("should stream SSE events", async () => {
const app = Bklar({ logger: false });
app.get("/events", (ctx) => {
const sse = ctx.sse();
sse.send("ping", JSON.stringify({ t: Date.now() }));
sse.close();
return undefined;
});
const res = await app.request("/events");
expect(res.headers.get("Content-Type")).toBe("text/event-stream");
const body = await res.text();
expect(body).toContain("event: ping");
});Mocking Services
import { spyOn } from "bun:test";
it("should handle db errors", async () => {
const spy = spyOn(db, "findUser").mockRejectedValue(new Error("DB Down"));
const res = await app.request("/users/1");
expect(res.status).toBe(500);
spy.mockRestore();
});# Run all tests
bun test
# Run specific file
bun test tests/app.test.ts# E2E tests use real ports — ensure they're available
bun test tests/e2e.test.ts