Skip to main content
Home

@cross/test@0.0.14
Built and signed on GitHub Actions

Write cross-runtime tests utilizing the built-in test runners of Deno, Node and Bun. Works great with @std/assert and Sinon.

This package works with Node.js, Deno, Bun
This package works with Node.js
This package works with Deno
This package works with Bun
JSR Score
100%
Published
a month ago (0.0.14)

Cross-runtime Testing for Deno, Bun, and Node.js

JSR Version JSR Score

A minimal, focused testing framework for writing tests that run identically across Deno, Bun, and Node.js. Part of the @cross suite - check out our growing collection of cross-runtime tools at github.com/cross-org.

Why @cross/test?

While node:test now works across runtimes, @cross/test provides unique advantages:

  • Unified Simple API - Single test() function with consistent behavior across all runtimes
  • JSR-First - Seamlessly works with JSR packages like @std/assert and @std/expect
  • Test Steps - Built-in context.step() support for organizing tests into sequential steps with shared state
  • Callback Support - Native waitForCallback option for callback-based async tests
  • Minimal Surface - Focused API that abstracts runtime differences without bloat

Installation

Install @cross/test along with the assertion library of your choice. We recommend using @std/assert for consistency across runtimes:

# Pick your runtime and package manager:
npx jsr add @cross/test @std/assert  # Node.js
deno add jsr:@cross/test jsr:@std/assert     # Deno
bunx jsr add @cross/test @std/assert # Bun

Examples

Simple tests with @std/assert

import { test } from "@cross/test";
import { assertEquals, assertNotEquals } from "@std/assert";

test("Multiplication", () => {
  assertEquals(5 * 4, 20);
});

test("Test with timeout", () => {
  assertEquals(5 * 4, 20);
}, { timeout: 1000 });

// Callback-based async test (unique to @cross/test)
test("Callback-based async", (_context, done) => {
  setTimeout(() => {
    assertNotEquals(5, 4);
    done();
  }, 500);
}, { waitForCallback: true });

Test steps for sequential workflows

Organize tests into steps with shared state - perfect for integration tests and workflows:

import { test } from "@cross/test";
import { assertEquals } from "@std/assert";

test("User registration flow", async (context) => {
  let userId;

  await context.step("Create user", () => {
    userId = createUser("john@example.com");
    assertEquals(typeof userId, "string");
  });

  await context.step("Verify user exists", () => {
    const user = getUser(userId);
    assertEquals(user.email, "john@example.com");
  });

  await context.step("Delete user", () => {
    deleteUser(userId);
    assertEquals(getUser(userId), null);
  });
});

Steps share the parent test's scope and execute sequentially, making complex test flows easy to write and debug.

Callback support in test steps

Just like tests, steps can also use callbacks for async operations with the waitForCallback option:

import { test } from "@cross/test";
import { assertEquals } from "@std/assert";

test("Test with callback-based steps", async (context) => {
  let completed = false;

  await context.step("Async operation", (_context, done) => {
    setTimeout(() => {
      completed = true;
      done();
    }, 100);
  }, { waitForCallback: true });

  assertEquals(completed, true);
});

Spying, mocking and stubbing using sinon

import { test } from "@cross/test";
import { assertEquals } from "@std/assert";
import sinon from "sinon";

// Prepare the "environment"
function bar() {/*...*/}
export const funcs = {
  bar,
};
export function foo() {
  funcs.bar();
}

test("calls bar during execution of foo", () => {
  const spy = sinon.spy(funcs, "bar");

  foo();

  assertEquals(spy.called, true);
  assertEquals(spy.getCalls().length, 1);
});

Running the tests

  • Node.js: node --test
  • Node.js (TS): npx tsx --test Remember { "type": "module" } in package.json
  • Deno: deno test
  • Bun: bun test

Configuring CI

  • Bun (GitHub Actions):
name: Bun CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: antongolub/action-setup-bun@v1.12.8
        with:
          bun-version: v1.x # Uses latest bun 1
      - run: bun x jsr add @cross/test @std/assert # Installs dependencies
      - run: bun test # Runs the tests
  • Deno (GitHub Actions):
name: Deno CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: denoland/setup-deno@v1
        with:
          deno-version: v1.x # Uses latest deno version 1
      - run: deno add @cross/test @std/assert # Installs dependencies from jsr.io
      - run: deno test # Runs tests
  • Node (GitHub actions):
name: Node.js CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 21.x]

    steps:
      - uses: actions/checkout@v3
      - run: npx jsr add @cross/test @std/assert
      - run: "echo '{ \"type\": \"module\" }' > package.json" # Needed for tsx to work
      - run: npx --yes tsx --test *.test.ts
Built and signed on
GitHub Actions

New Ticket: Report package

Please provide a reason for reporting this package. We will review your report and take appropriate action.

Please review the JSR usage policy before submitting a report.

Add Package

deno add jsr:@cross/test

Import symbol

import * as test from "@cross/test";
or

Import directly with a jsr specifier

import * as test from "jsr:@cross/test";

Add Package

pnpm i jsr:@cross/test
or (using pnpm 10.8 or older)
pnpm dlx jsr add @cross/test

Import symbol

import * as test from "@cross/test";

Add Package

yarn add jsr:@cross/test
or (using Yarn 4.8 or older)
yarn dlx jsr add @cross/test

Import symbol

import * as test from "@cross/test";

Add Package

vlt install jsr:@cross/test

Import symbol

import * as test from "@cross/test";

Add Package

npx jsr add @cross/test

Import symbol

import * as test from "@cross/test";

Add Package

bunx jsr add @cross/test

Import symbol

import * as test from "@cross/test";