Skip to content

bun test #1825

Open
Open

Description

@Electroid

Jest

describe()

test()

Lifecycle hooks

expect()

Mocks

Misc

Vitest

expect()

Mocks

Timers

Misc

jest-extended

expect()

Activity

  1. added
    trackingAn umbrella issue for tracking big features
    bun:testSomething related to the `bun test` runner
    on Jan 18, 2023
  2. changed the title [-]Promote `bun wiptest` to `bun test`[/-] [+]`bun test`[/+] on Jan 18, 2023
  3. useafterfree commented on Jan 18, 2023

    @useafterfree

    ts-jest does some magical hoisting with mock* functions which may or may not be a challenge with bun:
    swc-project/swc#5448 (comment)

  4. nskins commented on Mar 11, 2023

    @nskins
    Contributor

    This issue can be updated to check off these four (they were implemented in PR #1573):

    • .toBeGreaterThan(number | bigint)
    • .toBeGreaterThanOrEqual(number | bigint)
    • .toBeLessThan(number | bigint)
    • .toBeLessThanOrEqual(number | bigint)
  5. erikshestopal commented on Jun 26, 2023

    @erikshestopal

    Should jest.mock be added as well? Doesn't seem to be tracked.

  6. Jarred-Sumner commented on Jun 26, 2023

    @Jarred-Sumner
    Collaborator

    @erikshestopal please give it a try in the canary build, a lot of it is in there

  7. brapifra commented on Jun 27, 2023

    @brapifra

    jest.clearAllMocks() is also missing 👀 🙏

    image

  8. changed the title [-]`bun test`[/-] [+]`bun test` [Converted to "`bun test`" project][/+] on Jul 6, 2023
  9. changed the title [-]`bun test` [Converted to "`bun test`" project][/-] [+]`bun test`[/+] on Jul 6, 2023
  10. itsezc commented on Jul 7, 2023

    @itsezc

    Would there be any scope to add DOM matchers? If so, I'd love for some parity with the jest-dom library.

  11. Hanaasagi commented on Jul 7, 2023

    @Hanaasagi
    Contributor

    2023-07-07_10-56

    toThrowError is missing. It's a alias for toThrow.

    Ref: https://jestjs.io/docs/expect#tothrowerror

  12. riywo commented on Jul 10, 2023

    @riywo

    test.each and describe.each are super helpful to build data-driven unit tests.

  13. simylein commented on Jul 14, 2023

    @simylein
    Contributor

    I am proposing adding https://vitest.dev/api/expect-typeof.html to the vitest list.

  14. ghiscoding commented on Aug 8, 2023

    @ghiscoding

    test.each and describe.each are super helpful to build data-driven unit tests.

    It's being worked on in PR #4047

  15. 101 remaining items

  16. janhesters commented on Oct 24, 2025

    @janhesters

    Please add vi.onTestFinished to the list. A super useful hook for creating reusable logic, such as:

    import { onTestFinished } from 'vitest';
    
    import {
      createUserWithOrgAndAddAsMember,
      createUserWithTrialOrgAndAddAsMember,
      teardownOrganizationAndMember,
    } from './test-utils';
    
    /**
     * Creates a user with a trial organization and adds the user as a member of
     * the organization.
     * Also automatically tears down the organization and user after the test is
     * finished.
     *
     * @param args - The arguments to pass to the `createUserWithTrialOrgAndAddAsMember`
     * function.
     * @returns The organization and user.
     */
    export async function setupUserWithTrialOrgAndAddAsMember(
      ...args: Parameters<typeof createUserWithTrialOrgAndAddAsMember>
    ) {
      const { organization, user } = await createUserWithTrialOrgAndAddAsMember(
        ...args,
      );
    
      onTestFinished(async () => {
        await teardownOrganizationAndMember({ organization, user });
      });
    
      return { organization, user };
    }
    
    /**
     * Creates a user with an organization and adds the user as a member of the
     * organization.
     * Also automatically tears down the organization and user after the test is
     * finished.
     *
     * @param args - The arguments to pass to the `createUserWithOrgAndAddAsMember`
     * function.
     * @returns The organization and user.
     */
    export async function setupUserWithOrgAndAddAsMember(
      ...args: Parameters<typeof createUserWithOrgAndAddAsMember>
    ) {
      const { organization, user, subscription } =
        await createUserWithOrgAndAddAsMember(...args);
    
      onTestFinished(async () => {
        await teardownOrganizationAndMember({ organization, user });
      });
    
      return { organization, user, subscription };
    }

    Here is a temporary workaround:

    /**
     * Test lifecycle utilities for Bun tests.
     *
     * Provides a Vitest-like `onTestFinished` hook for automatic cleanup after tests.
     * Since Bun doesn't have a built-in equivalent to `onTestFinished`, this module
     * implements it using `AsyncLocalStorage` and `afterEach`.
     *
     * @example
     * ```ts
     * import { beforeEach, test } from 'bun:test';
     * import { initializeTestContext, onTestFinished } from './test-lifecycle';
     *
     * // Set up automatic cleanup for all tests in the file
     * beforeEach(() => {
     *   initializeTestContext();
     * });
     *
     * // Helper function that automatically cleans up after itself
     * async function setupUser(name: string) {
     *   const user = await createUser(name);
     *
     *   onTestFinished(async () => {
     *     await deleteUser(user.id);
     *   });
     *
     *   return user;
     * }
     *
     * test('example', async () => {
     *   const user = await setupUser('Alice');
     *   // ... test code ...
     *   // user will be automatically deleted after the test
     * });
     * ```
     */
    
    import { AsyncLocalStorage } from "node:async_hooks";
    
    import { afterEach } from "bun:test";
    
    // Create isolated storage per test execution context
    const cleanupStorage = new AsyncLocalStorage<Array<() => Promise<void>>>();
    
    // Store the current test's callbacks so afterEach can access them
    // even if it runs in a different async context
    let currentTestCallbacks: Array<() => Promise<void>> | null = null;
    
    /**
     * Register a cleanup function to run after the current test finishes.
     * This is similar to Vitest's `onTestFinished` hook.
     *
     * Note: This works in sequential test execution mode (Bun's default).
     * For concurrent test execution, each test should avoid shared mutable state.
     *
     * IMPORTANT: You must call `initializeTestContext()` at the start of each test
     * (or use a `beforeEach` hook to call it automatically).
     *
     * @param callback - The async cleanup function to run after the test
     *
     * @example
     * ```ts
     * import { beforeEach, test } from 'bun:test';
     * import { initializeTestContext, onTestFinished } from './test-lifecycle';
     *
     * beforeEach(() => {
     *   initializeTestContext();
     * });
     *
     * test('my test', async () => {
     *   const resource = await createResource();
     *   onTestFinished(async () => {
     *     await resource.cleanup();
     *   });
     *   // ... test code ...
     * });
     * ```
     */
    export function onTestFinished(callback: () => Promise<void>) {
    	const callbacks = cleanupStorage.getStore();
    	if (callbacks) {
    		callbacks.push(callback);
    	} else {
    		// This shouldn't happen in normal test execution
    		console.warn("onTestFinished called outside of test context");
    	}
    }
    
    /**
     * Initialize the test context for cleanup tracking.
     * This must be called at the start of each test to set up the isolated cleanup queue.
     *
     * TIP: You can call this in a `beforeEach` hook to avoid repeating it in every test:
     *
     * @returns The cleanup callbacks array (mainly for internal use)
     *
     * @example
     * ```ts
     * import { beforeEach, test } from 'bun:test';
     * import { initializeTestContext, onTestFinished } from './test-lifecycle';
     *
     * // Option 1: Call in beforeEach (recommended)
     * beforeEach(() => {
     *   initializeTestContext();
     * });
     *
     * test('my test', async () => {
     *   // initializeTestContext already called by beforeEach
     *   // ... test code ...
     * });
     *
     * // Option 2: Call manually in each test
     * test('my test', async () => {
     *   initializeTestContext();
     *   // ... rest of test ...
     * });
     * ```
     */
    export function initializeTestContext() {
    	const callbacks: Array<() => Promise<void>> = [];
    	cleanupStorage.enterWith(callbacks);
    	// Store reference for afterEach to access
    	currentTestCallbacks = callbacks;
    	return callbacks;
    }
    
    // Global afterEach hook to run all registered cleanup callbacks
    afterEach(async () => {
    	// Use the stored reference since afterEach may run in a different async context
    	if (currentTestCallbacks && currentTestCallbacks.length > 0) {
    		for (const cleanup of currentTestCallbacks) {
    			await cleanup();
    		}
    		// Clear the callbacks after running them
    		currentTestCallbacks.length = 0;
    	}
    	currentTestCallbacks = null;
    });
  17. pfgithub commented on Oct 24, 2025

    @pfgithub
    Contributor

    @janhesters We support afterAll inside the test callback, so until onTestFinished is added, you can use it as a workaround:

    test("test 1", () => {
        afterAll(() => {
            console.log("test 1 finished");
        });
        console.log("test 1 running");
    });
    test("test 2", () => {
        afterAll(() => {
            console.log("test 2 finished");
        });
        console.log("test 2 running");
    });
    a.test.js:
    test 1 running
    test 1 finished
    ✓ test 1 [0.26ms]
    test 2 running
    test 2 finished
    ✓ test 2 [0.04ms]
    

    However, this will run in a different order than onTestFinished. It will run before the test's afterEach hooks.

  18. janhesters commented on Oct 24, 2025

    @janhesters

    @pfgithub Interesting, thank you for sharing that!

  19. janhesters commented on Oct 26, 2025

    @janhesters

    Feature Request: Per-Test-File Preload Configuration

    Edit: Created #26693

    Use Case

    When testing full-stack applications, different test files often have conflicting requirements:

    • Component/Hook tests need a DOM environment (e.g., HappyDOM, JSDOM)
    • Server/API route tests need Node.js APIs without browser restrictions (e.g., setting Cookie headers on Request objects)

    Currently, the global preload in bunfig.toml applies to ALL tests, making it impossible to conditionally load environment-specific setup based on the test file being executed.

    Current Problem

    HappyDOM enforces browser security restrictions, including preventing JavaScript from setting Cookie headers on Request objects. This breaks authenticated route testing:

    // ❌ Fails with HappyDOM preloaded globally
    const headers = new Headers({ Cookie: "session=abc123" });
    const request = new Request("http://localhost/api/protected", { headers });
    // Cookie header is stripped by HappyDOM

    Current Workarounds

    1. Monkey-patch the Request constructor (what we're doing now)

      • ✅ Works, but feels hacky
      • ⚠️ Bypasses security model that HappyDOM intentionally enforces
    2. Separate test commands

      "test:components": "bun test **/*.component.test.ts --preload=./setup-dom.ts",
      "test:api": "bun test **/*.api.test.ts"
      • ❌ Slow: can't run all tests in one command
      • ❌ Maintenance burden: must remember which command for which tests

    Proposed Solution

    Expose test file information to preload scripts via environment variable:

    // conditional-preload.ts
    const testFile = process.env.BUN_TEST_FILE || "";
    
    if (testFile.endsWith(".component.test.ts") || testFile.includes("/use-")) {
      await import("./setup-dom.ts"); // Load HappyDOM
    }
    // Otherwise, run in Node.js environment

    Then in bunfig.toml:

    [test]
    preload = ["./conditional-preload.ts"]

    Alternative API

    Or provide a more explicit API:

    [test]
    preload = ["./setup-common.ts"]
    
    [[test.conditionalPreload]]
    pattern = "**/*.component.test.{ts,tsx}"
    preload = ["./setup-dom.ts"]
    
    [[test.conditionalPreload]]
    pattern = "**/*.api.test.ts"
    preload = ["./setup-api.ts"]

    Related

    • Similar to Jest's testEnvironment per-file: @jest-environment jsdom
    • Vitest supports per-file environment via docblock comments

    Would love to see this functionality in Bun! The test runner is already incredibly fast - this would make it even more versatile for full-stack testing. 🚀

  20. quantizor commented on Nov 1, 2025

    @quantizor

    @Jarred-Sumner is addSnapshotSerializer getting implemented soon? it's listed in the docs but says not implemented in code https://bun.sh/docs/test/snapshots#custom-serializers

  21. thi-marques commented on Nov 18, 2025

    @thi-marques

    I did not found any mention to jest.replaceProperty. It would be great to have it on the roadmap, it's very handy.

  22. Azarattum commented on Jan 28, 2026

    @Azarattum

    Would be nice to be able to use --cpu-prof with bun test.

  23. janhesters commented on Feb 3, 2026

    @janhesters

    @pfgithub created a PR for my second feature request, the per file configs here: #26693

  24. ivands commented on Feb 9, 2026

    @ivands

    Could we also add expect.addEqualityTesters.
    This is needed if you wanna add custom logic to check the equality of custom objects.
    For example: a custom BigFloat implementation.

  25. ansulagrawal commented on Apr 11, 2026

    @ansulagrawal

    @pfgithub and team, can we add counts and percentages here?

    For example:

    • Total Percentage: X/100% completed
    • Total Count: X/Y completed
    • Describe, Jest, etc., count

    Just a suggestion—what do you think?

  26. tim-soft commented on Apr 15, 2026

    @tim-soft

    @Electroid Last update to OP's post was ~6 months ago, is there a more up-to-date list for Bun's Jest support?

  27. added 3 commits that reference this issue on May 18, 2026
    952ab75
    c853324
    3aec8b8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bun:testSomething related to the `bun test` runnertrackingAn umbrella issue for tracking big features

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions