Jest to Deno mapping table
Jest constructs map to Deno as follows: describe/it/beforeEach map to node:test or @std/testing/bdd; expect(...) matchers map to jsr:@std/expect; jest.fn() maps to fn() from jsr:@std/expect; jest.spyOn(obj, "m") maps to spy/stub from @std/testing/mock; toMatchSnapshot() maps to assertSnapshot; jest.useFakeTimers() maps to FakeTime; npx jest maps to deno test; npx jest --watch maps to deno test --watch; npx jest -t "name" maps to deno test --filter "name".
Jest configuration migration to deno.json
Jest configuration mostly disappears in Deno because TypeScript, JSX, and ES modules work without transforms, so ts-jest, babel-jest, and transform entries have no equivalent. File selection moves into deno.json with test.include and test.exclude fields.
Jest to Deno test basic migration
Most Jest suites translate to deno test without rewriting test logic. The node:test module provides the same describe/it structure, and the standard library ships an expect with the matchers already used in Jest. The main changes are new imports and how the runner is configured.
Module mocks in Jest have no direct Deno equivalent
There is no equivalent of jest.mock("./module") in Deno because module records are immutable. Tests that rely on module mocking migrate to dependency injection (passing the collaborator in and providing a spy or stub to the test) or stubbing the object the module exposes with stub from @std/testing/mock. This is usually the only part of a migration that requires touching the code under test.
Deno test migration example with imports
A typical Jest test translated to Deno uses imports from node:test (beforeEach, describe, it) and jsr:@std/expect (expect, fn). The test logic remains unchanged except for using expect and fn instead of jest.fn(). The test is run with deno test command.
Deno test imports replace Jest globals
In Deno tests, describe, it, expect, and hooks are explicit imports from node:test and jsr:@std/expect, rather than injected as globals like in Jest. Tests written against node:test also work in Node.js itself.
Stubs replace implementations and record calls like spies
A stub replaces the original function implementation entirely, returning predetermined values or simulating errors. Stubs still record their calls, so all spy assertions work on them. Use stub(object, 'methodName', implementation) to replace a method. Stubs are disposable with the using keyword and can be manually restored with stub.restore().
returnsNext for stubs to return different values per call
When a stub should return different values on each call, pass returnsNext with an array of values in order. This simulates scenarios like missing records, retries, or call-by-call behavior. Example: stub(database, 'getUserById', returnsNext([undefined])) returns undefined on the first call.
Spy, stub, and mock terminology distinction
A spy observes—it records calls while the real behavior runs. A stub replaces—it records calls and substitutes a controlled implementation. A mock is the umbrella term for any test double; in practice, a mock object is usually a hand-built stand-in whose methods are spies or stubs, asserted against after the code under test runs.
FakeTime controls Date, setTimeout, setInterval for deterministic tests
FakeTime from jsr:@std/testing/time replaces Date, setTimeout, setInterval, and related functions with controllable versions. It lets you pin the current date and advance time instantly with tick() instead of waiting for real timeouts. FakeTime is disposable with the using keyword and automatically restores the real clock when the test ends.
FakeTime instantiation with a known date
Create a FakeTime instance with a known date: using time = new FakeTime(new Date('2023-05-01T12:00:00Z')). This pins the clock to a specific moment for deterministic testing.
time.tick() advances FakeTime by milliseconds
Call time.tick(milliseconds) to advance the fake clock. For example, time.tick(5 * 24 * 60 * 60 * 1000) advances 5 days. Timers fire as soon as the fake clock passes them.
Choose the simplest test double: spy, stub, or fake time
Use a spy if you only need to verify an interaction happened. Use a stub if the real implementation is slow, nondeterministic, or external. Use fake time for clock-dependent code instead of stubbing Date by hand. Stub at interface boundaries (like a deps object or database layer) rather than deep inside implementation details.
Spy example: testing database.save interaction
Example showing how to test a saveUser() function that calls database.save(). A spy wraps the save method to record calls without a real database. assertSpyCalls verifies the method was called exactly once, and assertSpyCall verifies the arguments passed. From jsr:@std/testing/mock.
FakeTime example: testing time-dependent and timeout code
Example showing FakeTime controlling isWeekend() and delayedGreeting() functions. The test pins the clock to Monday, advances 5 days to Saturday, and verifies a timeout fires when the fake clock passes 1000ms. FakeTime is declared with using and automatically restores the real clock.
Stub example: replacing getUserName implementation
Example showing how to stub a getUserName method to return 'Test User' instead of the original implementation. The stub is declared with using and automatically restores the original method when out of scope. The test verifies the function behaved correctly with the stubbed dependency.
Use using keyword for disposable spies, stubs, and fake time
Declare spies, stubs, and FakeTime instances with the using keyword to make them disposable. They automatically restore to their original state when they go out of scope, preventing state from leaking between tests. Alternatives like try/finally or manual restore() calls are available if using is not available.
Test doubles in @std/testing/mock and @std/testing/time
The Deno Standard Library provides test doubles in jsr:@std/testing/mock (spy, stub, returnsNext) and jsr:@std/testing/time (FakeTime). These modules contain everything needed for spies, stubs, mocks, and fake time testing.
Spies: wrap functions to record calls without changing behavior
A spy wraps a function and records every call, including how many times it ran and which arguments it received. Spies do not change the function's behavior, making them the lightest-touch test double available. Use spy(function) or spy(object, 'methodName') to create a spy. Spies are disposable with the using keyword and restore automatically when out of scope.
assertSpyCalls and assertSpyCall verify spy interactions
assertSpyCalls(spy, expectedCallCount) checks the total number of times a spy was called. assertSpyCall(spy, callIndex, { args: [...] }) inspects a single call by index to verify its arguments and return value.
Method spies with using keyword restore automatically
When spying on an existing method of an object, use the using keyword to declare the spy as disposable. The original method restores automatically when the spy goes out of scope. If using is not available, call spy.restore() in a finally block instead to prevent state leaking between tests.
Closing network connection resources example
Network connections should be closed when done with them. Example:
const conn = await Deno.connect({ hostname: "example.com", port: 80 });
// Do something with the connection
conn.close(); // <- Always close the connection when you are done with it
Exit sanitizer enabled by default
The exit sanitizer ensures that tested code doesn't call Deno.exit(), which could signal a false test success. This sanitizer is enabled by default but can be disabled with sanitizeExit: false.
Per-test sanitizer configuration example
You can enable sanitizers per-test by passing sanitizeOps and sanitizeResources options to Deno.test():
Deno.test({
name: "strict",
sanitizeOps: true,
sanitizeResources: true,
fn() {/* … */},
});
Resource sanitizer prevents I/O leaks
The resource sanitizer ensures that all I/O resources created during a test are closed to prevent leaks. I/O resources include Deno.FsFile handles, network connections, fetch bodies, timers, and other resources that are not automatically garbage collected.
Resource sanitizer opt-in in Deno 2.8
As of Deno 2.8, the resource sanitizer is off by default. You can enable it per-test with sanitizeResources: true, or use one of the global mechanisms (per-module with Deno.test.sanitizer(), CLI flags, environment variables, or deno.json).
Async operation sanitizer ensures all ops complete
The async operation sanitizer ensures that all async operations started in a test are completed before the test ends. This prevents tests from ending before async operations are finished, which could mask failures.
Async operation sanitizer opt-in in Deno 2.8
As of Deno 2.8, the async operation sanitizer is off by default. You can enable it per-test with sanitizeOps: true, or use one of the global mechanisms (per-module with Deno.test.sanitizer(), CLI flags, environment variables, or deno.json).
Sanitizers hierarchy of precedence
Sanitizer settings can be configured at five scopes with the following precedence from highest to lowest: per-test, per-module with Deno.test.sanitizer(), CLI flags (--sanitize-ops and --sanitize-resources), environment variables (DENO_TEST_SANITIZE_OPS=1 and DENO_TEST_SANITIZE_RESOURCES=1), and deno.json configuration.
Per-module sanitizer configuration with Deno.test.sanitizer()
You can enable sanitizers for all tests in a module using Deno.test.sanitizer():
Deno.test.sanitizer({ ops: true, resources: true });
Deno.test("uses module-level sanitizers", () => {/* … */});
deno.json sanitizer configuration
You can enable sanitizers globally via deno.json:
{
"test": {
"sanitizeOps": true,
"sanitizeResources": true
}
}
Closing file resources example
Files should be closed when done with them. Example:
const file = await Deno.open("hello.txt");
// Do something with the file
file.close(); // <- Always close the file when you are done with it
Canceling fetch body resources example
Fetch response bodies should be canceled when not consumed. Example:
const response = await fetch("https://example.com");
// Do something with the response
await response.body?.cancel(); // <- Always cancel the body when you are done with it, if you didn't consume it otherwise
Multiple hooks of same type
You can register multiple hooks of the same type and they will execute in the order specified: beforeEach hooks in FIFO order, afterEach hooks in LIFO order.
Deno.test vs node:test support
Deno supports two test APIs equally: Deno.test and Node's built-in node:test module. Both are first-class and deno test discovers, runs, and reports tests written with either one. Core features like coverage and name filtering work the same regardless of which you use, and you can mix both in the same project. Neither is more supported than the other.
Deno.test API advantages
Deno.test needs no import and exposes Deno-specific options such as per-test permissions and the op/resource sanitizer toggles. Use Deno.test when you want Deno-native ergonomics and options.
node:test API advantages
node:test uses the Node testing API, so a suite written with it also runs unchanged on Node.js. Use node:test when you want a suite that is portable across Deno and Node, or when you're migrating a Node project.
node:test mocking utilities in Deno
node:test's mocking utilities work in Deno: mock.timers provides fake timers for setTimeout, setInterval, Date, and the node:timers modules, and mock.module replaces a module's exports for the duration of a test. Suites that rely on them run unchanged.
Basic Deno.test syntax
Tests are defined using Deno.test(). The simplest form is Deno.test(name, fn) where name is a string and fn is a function. Tests can also be async. A full form is Deno.test({ name, fn }) passing an options object.
deno test default glob pattern
If run without a file name or directory name, deno test automatically finds and executes all tests in the current directory recursively that match the glob {*_,*.,}test.{ts,tsx,mts,js,mjs,jsx}. Additionally, any script file inside a directory named __tests__ is treated as a test file, regardless of its file name.
deno test command examples
Run all tests in current directory: deno test. Run tests in specific directory: deno test util/. Run specific file: deno test my_test.ts. Run in parallel: deno test --parallel. Pass additional arguments visible in Deno.args: deno test my_test.ts -- -e --foo --bar. Provide permissions: deno test --allow-read=. my_test.ts.
Test steps syntax
Test steps break down tests into smaller, manageable parts. Use await t.step(name, fn) within a test, where t is the test context parameter. This is useful for setup and teardown operations within a test.
Test timeout configuration
Set a maximum duration for individual tests using the timeout option in milliseconds. If a test exceeds its deadline it is marked as failed. Both asynchronous hangs (a promise that never resolves) and synchronous hot loops are caught. Setting timeout to 0 or omitting it means the test runs without a deadline. If a test times out the next test in the same file still runs normally.
Test retry and repeats options
retry re-runs a failing test and passes if any attempt passes, useful for tolerating flaky tests. repeats runs the test several times and requires every run to pass, useful for catching flakiness. The two options compose, so each repetition may itself be retried. Every attempt re-runs beforeEach and afterEach hooks and captures a fresh leak-check baseline.
Global retry and repeats defaults
The --retry and --repeats flags set a default for the whole test run. A test that sets its own option takes precedence, including an explicit 0 that opts the test out of a flag-provided default. Example: deno test --retry=2.
Deno.test.each parameterized tests
Deno.test.each runs the same test body over a table of cases, registering one real test per case so each case reports independently and can be filtered or run on its own. Array cases are spread as positional arguments. The name template interpolates case values with printf-style tokens (%s, %d/%i, %f, %j, %o/%O) consumed in order, plus %# for the zero-based case index. Object or primitive cases are passed as a single argument; for object cases, $key and $key.nested in the template interpolates the matching property.
Test hooks: beforeAll, beforeEach, afterEach, afterAll
Deno provides four test hooks: Deno.test.beforeAll(fn) runs once before all tests in the current scope, Deno.test.beforeEach(fn) runs before each individual test, Deno.test.afterEach(fn) runs after each individual test, and Deno.test.afterAll(fn) runs once after all tests in the current scope.
Test hook execution order
beforeAll/beforeEach hooks execute in FIFO (first in, first out) order. afterEach/afterAll hooks execute in LIFO (last in, first out) order. If an exception is raised in any hook, remaining hooks of the same type will not run, and the current test will be marked as failed.
Snapshot testing in Deno
Compare a value against a serialized reference stored next to your test, and update the references with a single flag when behavior intentionally changes.
deno test --filter flag
Run a subset of tests with the --filter flag. It accepts a string to match test names containing that string, or a regular expression wrapped in forward slashes. Example: deno test --filter "my" tests/ runs tests whose name contains "my". Example: deno test --filter "/test-*\d/" tests/ runs tests whose name matches the pattern.
Test file collection filtering with deno.json
To control which test files are collected in the first place, set test.include and test.exclude in your configuration file (deno.json).
deno test --changed for git-based test selection
deno test --changed runs only the test modules affected by files you have changed in git. With no value it looks at the working tree including staged, unstaged, and untracked files. Pass a git ref to also include everything committed since you branched off it. Deno compares against the merge-base (the <ref>...HEAD three-dot form), so it captures the full set of changes on your branch rather than just the latest commit. Example: deno test --changed=origin/main runs every test affected since branching off main.
deno test --related for dependency-based test selection
deno test --related runs the test modules that depend on the source files you name, without consulting git at all. Example: deno test --related=src/util.ts runs the tests that import src/util.ts, directly or transitively.
Test selection via --changed and --related logic
For both --changed and --related flags, Deno builds the module graph of the collected test files and keeps only the tests that reach a changed or named file through their imports. A test in cart_test.ts runs when it imports cart.ts and cart.ts changed, even if that import is several modules deep; a test that imports none of the affected files is skipped. The flags compose with the rest of test selection.
Test ignore option
You can ignore certain tests based on specific conditions using the ignore boolean in the test definition. If ignore is set to true, the test will be skipped. You can use Deno.test.ignore(name, fn) shorthand to ignore a test without passing any conditions.
Test only option
Use the only option to run only the tests with only set to true. Multiple tests can have this option set. You can use Deno.test.only(name, fn) shorthand. If any test is flagged with only, the overall test run will always fail, as this is intended to be a temporary measure for debugging.
deno test --fail-fast flag
Use deno test --fail-fast to stop execution after the first test failure. This is useful when you have a long-running test suite and want to stop on the first failure.
deno test reporters
Test output defaults to the detailed pretty reporter. Switch formats with the --reporter flag (dot, junit, tap), or write a JUnit XML report to a file with --junit-path while keeping readable output in the terminal. Example: deno test --reporter=dot. Example: deno test --junit-path=./report.xml.
Test coverage with deno test
Collect coverage while testing with deno test --coverage, then turn it into terminal, HTML, or lcov reports with deno coverage. The data comes straight from V8.
Behavior-Driven Development with @std/testing/bdd
With the @std/testing/bdd module you can write tests in a familiar format for grouping tests and adding setup/teardown hooks used by other JavaScript testing frameworks like Jasmine, Jest, and Mocha. The describe function creates a block that groups together several related tests. The it function registers an individual test case.