Test
test defines a test case. It supports chainable modifiers and fixture extension for flexible and powerful test definitions.
Alias: it.
test
- Type:
Defines a test case.
TestOptions
Pass a TestOptions object as the second argument (before the test function) to tune the behavior of a single test:
As a shorthand, you can still pass a number as the last argument to set only the timeout (equivalent to { timeout: n }):
TestOptions accepts:
timeout?: number— per-test timeout in milliseconds. Overridestest.testTimeout.retry?: number— re-runs the test up to this many times if it fails, stopping at the first pass. Overridestest.retry.repeats?: number— re-runs an already-passing test this many extra times; any failure marks the whole case as failed. Each repeat runs the fullbeforeEach/afterEachlifecycle and gets an independentretrybudget.meta?: TaskMeta— added in 0.11.1. Initial JSON-serializable metadata for the test result. If the test is inside adescribewithmeta, it inherits a copy of the suite metadata and test-level keys override inherited keys.
TaskMeta and TaskMetaValue are exported from @rstest/core and allow JSON-serializable values:
In this example, the test result's metadata starts as { owner: 'team-a' }. During execution, the test mutates the same object through context.task.meta, so reporters and the programmatic API receive { owner: 'team-a', startedBy: 'runtime' } on TestResult.meta.
test.each and test.for accept the same options as their second argument and apply it to every generated case.
test.only
Only run certain tests in a test file.
test.skip
Skips certain tests.
Use test.skip when you know at definition time that a test should be skipped. If the decision can only be made while the test is running, call context.skip() from the test context instead. context.skip() stops executing the current test immediately, so code after it will not run, and the test is reported as skipped.
test.todo
Marks certain tests as todo.
test.each
- Type:
Runs the same test logic for each item in the provided array.
You can also use a tagged template literal table syntax for more readable parameterized tests:
The first row defines the parameter names (column headers), and each subsequent row provides the values via template expressions (${...}). Columns are separated by |.
Since the table values are untyped by default, you can provide an explicit generic type parameter for type safety:
You can inject parameters with printf formatting in the test name in the order of the test function parameters.
%s: String%d: Number%i: Integer%f: Floating point value%j: JSON%o: Object%#: 0-based index of the test case%$: 1-based index of the test case%%: Single percent sign ('%')
You can also access object properties with $ prefix:
test.for
- Type:
Alternative to test.each to provide TestContext.
test.for also supports the tagged template literal table syntax:
You can provide an explicit generic type parameter for type safety:
test.fails
Marks the test as expected to fail.
test.concurrent
Runs the test concurrently with consecutive concurrent flags.
test.sequential
Runs the test sequentially (default behavior).
test.runIf
Runs the test only if the condition is true.
test.skipIf
Skips the test if the condition is true.
test.extend
- Type:
test.extend(fixtures: Fixtures) | test.extend(name, fixture) | test.extend(name, { scope: 'file' }, fixture)
Extends the test context with custom fixtures and returns a new test API. The original test is not modified — you can have multiple independent extended versions at the same time.
Fixtures are reusable context entries that help you prepare test resources once and inject them where needed. Typical uses include:
- Sharing test data and helper clients (for example, API clients, tokens, test users).
- Wrapping setup/teardown logic in one place instead of repeating it in every test.
- Building fixture dependencies (one fixture can consume another fixture).
- Running global-per-test side effects automatically (for example, logging) via
autofixtures.
The returned API has the same chainable modifiers as test (only, skip, each, concurrent, etc.) and can call .extend() again for further extension.
Fixtures object form
A fixture function in the fixtures object form receives two parameters:
- context — contains other fixtures as well as
TestContext(task,expect,onTestFinished,onTestFailed). Use object destructuring to declare the dependencies you need. - use — call
await use(value)to pass the fixture value to the test.
Fixture-aware callbacks must list every requested fixture explicitly through direct object destructuring in the callback parameter. Rstest does not infer dependencies from destructuring inside the function body. Object rest properties such as ({ db, ...rest }) and default values such as ({ db = fallback }) or ({ db } = {}) are not supported in test callbacks, fixture functions, or per-test hooks.
Code before await use(value) is setup; code after it is teardown (runs after the test finishes).
Named fixture form
The named fixture form returns a fixture value directly instead of calling use. Its second parameter provides onCleanup, which registers one callback for the fixture. Both the fixture function and cleanup callback may be asynchronous.
The two-argument named fixture form is test-scoped: Rstest evaluates fixture functions for each test attempt and runs their cleanup after the test and its per-test hooks finish. Plain values are reused across attempts. Use a fixture function when mutable state must be isolated for each attempt.
The fixture name must be a statically known ASCII JavaScript identifier so TypeScript can expose exactly one new context field. Values typed as string, patterned template literals such as `slot${string}`, names that require quoted destructuring such as base-url, and reserved test context fields are not accepted. Names that overlap Function properties, such as name and length, are supported.
A function or class passed directly is treated as a fixture function because both are JavaScript functions at runtime. To use the function or class itself as the fixture value, return it from a fixture function, for example .extend('predicate', () => predicate) or .extend('Service', () => Service). Use direct object destructuring in the first parameter to request other fixtures or fields from TestContext.
File-scoped named fixtures
Pass { scope: 'file' } to lazily create one fixture instance for the current test file. The instance is shared by every test, retry, repeat, and concurrent test that requests it. Its cleanup runs after all tests and afterAll hooks finish. Dependencies are initialized in order and cleaned up in reverse order.
File-scoped fixtures must be declared at the top level of the test file. They can depend only on file-scoped fixtures declared earlier in the chain: they do not receive test-scoped fixtures or TestContext. A file-scoped fixture cannot be overridden by a later .extend() call. Test-scoped fixtures can depend on file-scoped fixtures.
Fixture setup is bounded by the timeout of the test or hook that requests it. File cleanup is guarded by the Node or browser host, so a cleanup that never settles fails the file instead of blocking the run indefinitely.
Plain value fixtures
If a fixture does not need setup/teardown logic, you can provide a plain value directly:
Accessing TestContext
The first parameter of a fixture function also includes TestContext, so you can read current test information or use expect directly inside a fixture:
Fixture dependencies
A fixture can destructure other fixtures from its first parameter. Rstest automatically initializes them in dependency order and runs teardown in reverse order:
Using fixtures in hooks
beforeEach, afterEach, and a cleanup function returned by beforeEach can request fixtures. Rstest initializes fixtures requested by the test callback before beforeEach, preserving the existing test setup order. A fixture requested only by a hook is initialized before that hook runs. The same instance is shared for the rest of the test attempt, then torn down after all per-test hooks finish.
Declare hook fixture dependencies directly in the callback parameter, for example beforeEach(({ db }) => {}). A named hook context such as beforeEach((context) => {}) remains valid for accessing the regular TestContext, but destructuring context inside the function body does not initialize lazy fixtures.
Core hooks are suite-level APIs, so provide the fixture context type explicitly and register the hook in a suite whose tests use the matching extended test API. If a test in the suite does not provide a requested fixture, Rstest fails that test before invoking the hook and reports the missing fixture:
Automatic fixtures (auto)
Fixtures are lazy by default: they only run when requested through object destructuring by a test or per-test hook callback (or required by another fixture). To make a fixture run for every test automatically — even when no callback requests it — use the tuple syntax with { auto: true }:
Type inference and explicit generics
Fixture types are usually inferred automatically. If inference is not precise enough, provide an explicit generic to test.extend:
Fixture types only take effect on the new API returned by test.extend. The type signature of the original test remains unchanged.
Chainable modifiers
test supports chainable modifiers, so you can use them together. For example:
test.only.runIf(condition)(ortest.runIf(condition).only) will only run the test block if the condition is true.test.skipIf(condition).concurrent(ortest.concurrent.skipIf(condition)) will skip the test block if the condition is true, otherwise run the tests concurrently.test.runIf(condition).concurrent(ortest.concurrent.runIf(condition)) will only run the test block concurrently if the condition is true.test.only.concurrent(ortest.concurrent.only) will only run the test block concurrently.test.for(cases).concurrent(ortest.concurrent.for(cases)) will run the test block concurrently for each case in the provided array.- ......
Types
TestContext
TestContext provides some APIs, context information, and custom fixtures related to the current test.
Use context.task.retryCount to read the current retry index from tests, hooks, and fixtures. It is 0 for the initial attempt, 1 for the first retry, and resets to 0 for each run configured through repeats.
Use context.task.meta to attach JSON-serializable metadata to the current test result. You can mutate the metadata object or replace it with a new metadata object. Custom reporters and the programmatic API can read this metadata from TestResult.meta:
Use context.skip() to skip a test while it is running. Code after
context.skip() will not execute, and the test is reported as skipped:
You can also extend TestContext with custom fixtures using test.extend.