Pass context to Jest hooks and tests.
$ npm i -D jest-ctx
$ yarn add -D jest-ctx
jest-ctx exports the same module interface as @jest/globals, so it is easy
to drop into your codebase:
import {
afterAll,
afterEach,
beforeAll,
beforeEach,
describe,
expect,
jest,
test // aliases such as `it` are available as well.
} from 'jest-ctx'
If you would like to inject jest-ctx into the global environment, modify your
Jest config to disable injectGlobals
and then add a file to setupFilesAfterEnv
with the following:
import * as globals from 'jest-ctx'
Object.assign(globalThis, globals)
Context can be anything you like including objects, arrays, and primitives. It
is undefined by default. To give some context, use a beforeAll or
beforeEach hook.
You can set the context of any test group (top-level or described) by returning
data from a beforeAll hook:
beforeAll(() => '🍐');
it('has group context', (ctx) => expect(ctx).toEqual('🍐'));
beforeAll hooks receive context as their first argument, which allows for
accumulation:
beforeAll(() => '🍐');
beforeAll((ctx) => ctx + '🍐');
it('accumulates group context', (ctx) => expect(ctx).toEqual('🍐🍐'));
beforeAll hooks of described groups inherit the context of their ancestors:
beforeAll(() => '🍐');
describe('group one', () => {
beforeAll((ctx) => ctx + '🍎');
it('inherits group context', (ctx) => expect(ctx).toEqual('🍐🍎'));
})
describe('group two', () => {
beforeAll((ctx) => ctx + '🍊');
it('inherits group context', (ctx) => expect(ctx).toEqual('🍐🍊'));
})
You can set the isolated context of each test in a group by returning data from
a beforeEach hook:
const sequence = [...'🍋🍉'];
beforeEach(() => sequence.shift());
it('has test context one', (ctx) => expect(ctx).toEqual('🍋'));
it('has test context two', (ctx) => expect(ctx).toEqual('🍉'));
beforeEach hooks can accumlate context:
beforeEach(() => '🍋');
beforeEach((ctx) => ctx + '🍉');
it('accumulates test context', (ctx) => expect(ctx).toEqual('🍋🍉'));
beforeEach hooks inherit group context:
beforeAll(() => '🍐');
beforeEach((ctx) => ctx + '🍋');
it('inherits group context' (ctx) => expect(ctx).toEqual('🍐🍋'));
beforeEach hooks of described groups inherit context from their ancestors:
beforeEach(() => '🍋');
describe('group one', () => {
beforeEach((ctx) => ctx + '🍉');
it('inherits test context', (ctx) => expect(ctx).toEqual('🍋🍉'));
})
describe('group two', () => {
beforeEach((ctx) => ctx + '🥝');
it('inherits test context', (ctx) => expect(ctx).toEqual('🍋🥝'));
})
test hooks receive context as their first argument. Return values do not affect
context.
beforeAll(() => '🍐');
beforeEach((ctx) => ctx + '🍋');
it('has group and test context' (ctx) => expect(ctx).toEqual('🍐🍋'));
afterAll hooks receive context as their first argument. Note that this will
be the context of the group and will not include any isolated test context
created by beforeEach. If you need test context, see the afterEach hook.
Return values do not affect context.
beforeAll(() => '🍐');
beforeEach((ctx) => ctx + '🍋');
afterAll((ctx) => expect(ctx).toEqual('🍐'));
afterEach hooks receive context as their first argument. Return values do not
affect context.
beforeAll(() => '🍐');
beforeEach((ctx) => ctx + '🍋');
afterEach((ctx) => expect(ctx).toEqual('🍐🍋'));
Context can be accumulated to achieve many different scenarios.
Make sure you have a good grasp of Jest's
scoping and order of execution
when mixing beforeAll and beforeEach:
beforeAll(() => '🍐');
beforeEach((ctx) => ctx + '🍋');
it('has group and test context', (ctx) => expect(ctx).toEqual('🍐🍋'));
describe('group one', () => {
beforeAll((ctx) => ctx + '🍎');
beforeEach((ctx) => ctx + '🍉');
it('has group and test context', (ctx) => expect(ctx).toEqual('🍐🍎🍋🍉'));
})
describe('group two', () => {
beforeAll((ctx) => ctx + '🍊');
beforeEach((ctx) => ctx + '🥝');
it('has group and test context', (ctx) => expect(ctx).toEqual('🍐🍊🍋🥝'));
})
Any style of data management is allowed, so you are free to mutate your contexts or keep them immutable, even freeze them, seal them, etc.
Context is only set when a value is returned from beforeAll or beforeEach.
Async functions, promises, and timeouts work as usual. Note that hooks do not
receive a done callback like their native counterparts. As an alternative to
done, try using a pattern like this:
it('can be done', (ctx) => new Promise((done) => {
asyncCode(ctx, done)
}))
If it's got to be done with done, please open an issue and let's figure it out.
test.each and its variations receive context as the first argument followed
by the usual args for each table row:
beforeAll(() => '🍍')
it.each(['🥥'])(`has context and table data`, (ctx, data) => {
expect(ctx).toEqual('🍍')
expect(data).toEqual('🥥')
})
test.concurrent and its variations are affected by a known issue with
beforeEach (jestjs/jest#7997)
Types should work as usual except that hooks and tests can now receive and return context:
beforeAll((): string => '🫐');
it('has typed context', (ctx: string) => expect(ctx).toMatch('🫐'));
jest-ctx is available as open source under the terms of the MIT License.