> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/zhcndoc/bun/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing Tests

> Learn how to write tests with Bun's Jest-compatible test runner

Bun's test runner provides a Jest-compatible API for writing tests. If you're familiar with Jest, Vitest, or other test frameworks, you'll feel right at home.

## Basic test structure

Tests are defined using `test()` or `it()` functions:

```ts theme={null}
import { test, expect } from "bun:test";

test("addition works", () => {
  expect(1 + 1).toBe(2);
});

// "it" is an alias for "test"
it("subtraction works", () => {
  expect(5 - 3).toBe(2);
});
```

## Grouping tests with describe

Use `describe()` to group related tests:

```ts theme={null}
import { describe, test, expect } from "bun:test";

describe("Math operations", () => {
  test("addition", () => {
    expect(1 + 1).toBe(2);
  });

  test("subtraction", () => {
    expect(5 - 3).toBe(2);
  });

  describe("multiplication", () => {
    test("positive numbers", () => {
      expect(2 * 3).toBe(6);
    });

    test("negative numbers", () => {
      expect(-2 * 3).toBe(-6);
    });
  });
});
```

## Async tests

Tests can be async. Bun will wait for the promise to resolve:

```ts theme={null}
import { test, expect } from "bun:test";

test("async test", async () => {
  const response = await fetch("https://example.com");
  expect(response.status).toBe(200);
});

test("promise test", () => {
  return fetch("https://example.com").then(response => {
    expect(response.status).toBe(200);
  });
});
```

## Test timeout

By default, tests timeout after 5 seconds. You can customize this:

```ts theme={null}
import { test, expect } from "bun:test";

// Set timeout for a specific test
test("slow operation", async () => {
  await slowOperation();
}, 10000); // 10 second timeout

// Set timeout globally in bunfig.toml
// [test]
// timeout = 10000
```

## Skipping tests

Skip tests with `.skip()` or `test.skip()`:

```ts theme={null}
import { test, expect } from "bun:test";

// Skip a single test
test.skip("this test will be skipped", () => {
  expect(true).toBe(false);
});

// Alternative syntax
test("also skipped", () => {
  expect(true).toBe(false);
}).skip();

// Skip a describe block
describe.skip("skipped suite", () => {
  test("will not run", () => {
    // ...
  });
});
```

## Only running specific tests

Run only specific tests with `.only()`:

```ts theme={null}
import { test, expect } from "bun:test";

test.only("only this test will run", () => {
  expect(true).toBe(true);
});

test("this test will be skipped", () => {
  expect(true).toBe(true);
});
```

You can also use the `--only` flag to run only tests marked with `.only()` across your entire test suite:

```bash theme={null}
$ bun test --only
```

## Todo tests

Mark tests as todo when you're planning to implement them:

```ts theme={null}
import { test } from "bun:test";

test.todo("implement this feature");

test.todo("test this edge case", () => {
  // Test implementation
});
```

## Concurrent tests

Run tests concurrently to speed up test execution:

```ts theme={null}
import { test, expect } from "bun:test";

test.concurrent("test 1", async () => {
  await fetch("https://api1.example.com");
});

test.concurrent("test 2", async () => {
  await fetch("https://api2.example.com");
});

// Run all tests in a describe block concurrently
describe.concurrent("API tests", () => {
  test("endpoint 1", async () => {
    // ...
  });
  
  test("endpoint 2", async () => {
    // ...
  });
});
```

## Expectations

Bun provides a Jest-compatible `expect()` API:

```ts theme={null}
import { test, expect } from "bun:test";

test("matchers", () => {
  // Equality
  expect(2 + 2).toBe(4);
  expect([1, 2]).toEqual([1, 2]);
  
  // Truthiness
  expect(true).toBeTruthy();
  expect(false).toBeFalsy();
  expect(null).toBeNull();
  expect(undefined).toBeUndefined();
  
  // Numbers
  expect(10).toBeGreaterThan(5);
  expect(5).toBeLessThan(10);
  expect(0.1 + 0.2).toBeCloseTo(0.3);
  
  // Strings
  expect("hello world").toMatch(/world/);
  expect("hello").toContain("ell");
  
  // Arrays
  expect([1, 2, 3]).toContain(2);
  expect([1, 2, 3]).toHaveLength(3);
  
  // Objects
  expect({ a: 1, b: 2 }).toHaveProperty("a");
  expect({ a: 1, b: 2 }).toMatchObject({ a: 1 });
  
  // Exceptions
  expect(() => {
    throw new Error("fail");
  }).toThrow("fail");
  
  // Async
  await expect(Promise.resolve(42)).resolves.toBe(42);
  await expect(Promise.reject("error")).rejects.toBe("error");
});
```

## Negation

Negate any matcher with `.not`:

```ts theme={null}
import { test, expect } from "bun:test";

test("negation", () => {
  expect(1).not.toBe(2);
  expect([1, 2]).not.toContain(3);
});
```

## Custom matchers

Extend expect with custom matchers:

```ts theme={null}
import { expect } from "bun:test";

expect.extend({
  toBeWithinRange(received: number, min: number, max: number) {
    const pass = received >= min && received <= max;
    return {
      pass,
      message: () =>
        pass
          ? `expected ${received} not to be within range ${min} - ${max}`
          : `expected ${received} to be within range ${min} - ${max}`,
    };
  },
});

test("custom matcher", () => {
  expect(5).toBeWithinRange(1, 10);
});
```

## Test context

You can optionally pass a test context object with the `done` callback:

```ts theme={null}
import { test, expect } from "bun:test";

test("using done callback", (done) => {
  setTimeout(() => {
    expect(true).toBe(true);
    done();
  }, 100);
});
```

<Note>
  Prefer using async/await over done callbacks when possible, as it's more readable and easier to debug.
</Note>

## Parameterized tests

Run the same test with different inputs:

```ts theme={null}
import { test, expect } from "bun:test";

test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])("%i + %i should equal %i", (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// With objects
test.each([
  { a: 1, b: 1, expected: 2 },
  { a: 1, b: 2, expected: 3 },
  { a: 2, b: 1, expected: 3 },
])("$a + $b = $expected", ({ a, b, expected }) => {
  expect(a + b).toBe(expected);
});
```

## Related

* [Lifecycle Hooks](/test/lifecycle)
* [Mocks](/test/mocks)
* [Snapshots](/test/snapshots)
* [Configuration](/test/configuration)
