DEV Community

Alex Spinov
Alex Spinov

Posted on

Bun Test Has a Free Built-in Test Runner — Here's Why You Don't Need Jest Anymore

Jest takes 10 seconds to start. Bun's test runner starts in milliseconds. And it is built right in — no install needed.

Quick Start

# No installation needed — bun has it built in
bun test
Enter fullscreen mode Exit fullscreen mode
// math.test.ts
import { expect, test, describe, beforeEach, mock } from "bun:test";

describe("math", () => {
  test("addition", () => {
    expect(2 + 2).toBe(4);
  });

  test("multiplication", () => {
    expect(3 * 7).toBe(21);
  });
});
Enter fullscreen mode Exit fullscreen mode

Jest-Compatible API

import { expect, test, describe, beforeEach, afterEach, beforeAll, afterAll } from "bun:test";

// All Jest matchers work
expect(value).toBe(expected);
expect(value).toEqual(expected);
expect(value).toBeTruthy();
expect(value).toContain(item);
expect(value).toThrow();
expect(value).toMatchSnapshot();
expect(value).toHaveBeenCalled();
expect(value).toHaveBeenCalledWith(args);
Enter fullscreen mode Exit fullscreen mode

Mocking

import { mock, spyOn } from "bun:test";

// Mock a function
const mockFn = mock(() => 42);
mockFn();
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveReturnedWith(42);

// Spy on an object method
const obj = { method: () => "original" };
const spy = spyOn(obj, "method").mockReturnValue("mocked");
expect(obj.method()).toBe("mocked");

// Mock a module
mock.module("./api", () => ({
  fetchUsers: () => Promise.resolve([{ id: 1, name: "Alice" }]),
}));
Enter fullscreen mode Exit fullscreen mode

Async Testing

test("fetches data", async () => {
  const response = await fetch("https://api.example.com/users");
  const data = await response.json();
  expect(data).toBeArray();
  expect(data.length).toBeGreaterThan(0);
});

test("handles timeout", async () => {
  const promise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error("timeout")), 100);
  });
  expect(promise).rejects.toThrow("timeout");
});
Enter fullscreen mode Exit fullscreen mode

Snapshot Testing

test("user object snapshot", () => {
  const user = createUser("Alice", 30);
  expect(user).toMatchSnapshot();
});

test("inline snapshot", () => {
  expect({ a: 1, b: 2 }).toMatchInlineSnapshot(`
    {
      "a": 1,
      "b": 2,
    }
  `);
});
Enter fullscreen mode Exit fullscreen mode

DOM Testing (with happy-dom)

// bunfig.toml
// [test]
// preload = ["happy-dom"]

test("renders component", () => {
  document.body.innerHTML = '<div id="app"></div>';
  const app = document.getElementById("app");
  app!.innerHTML = "<h1>Hello</h1>";
  expect(document.querySelector("h1")!.textContent).toBe("Hello");
});
Enter fullscreen mode Exit fullscreen mode

CLI Options

# Run specific file
bun test auth.test.ts

# Run tests matching pattern
bun test --grep "should handle"

# Watch mode
bun test --watch

# Coverage
bun test --coverage

# Timeout
bun test --timeout 10000

# Bail on first failure
bun test --bail
Enter fullscreen mode Exit fullscreen mode

Speed Comparison

Project: 500 test files, 3000 tests
Jest:     35 seconds
Vitest:   12 seconds
Bun Test: 3 seconds
Enter fullscreen mode Exit fullscreen mode

Bun Test vs Jest vs Vitest

Feature Bun Test Jest Vitest
Speed Fastest Slow Fast
Install Built-in npm install npm install
TypeScript Native ts-jest Native
ESM Native Experimental Native
Snapshots Yes Yes Yes
Mocking Yes Yes Yes
Coverage Yes Istanbul c8/Istanbul
Watch Yes Yes Yes

Testing your web scraping code? Check out my Apify actors — tested and production-ready scrapers. For custom solutions, email spinov001@gmail.com.

Top comments (0)