Hooks
Note
This article describes test hooks. Read the Request Interception guide for more information on request hooks. Read the Modify Reporter Output guide for more information on reporter hooks.
Hooks are functions that run immediately before or immediately after other test entities. You can attach hooks to the following test entities: tests, fixtures, and test runs.
“Before” hooks often prepare the testing environment (e.g. authenticate the user) for future tests. Likewise, “after” hooks often ‘reset’ the testing environment (e.g. remove a database object) after the end of a test.
You can access test and fixture data inside the hooks, and even share variables between hooks and test code.
To re-use setup and teardown code, attach a single hook to multiple entities of the same kind.
Table of Contents
- Local Hooks and Global Hooks
- Hooks before and after test runs
- Hooks before and after fixtures
- Hooks before and after tests
- Hook Order and Priority
- Share Data Between Hooks and Test Code
- Access Fixture and Test Data in Hooks
Local Hooks and Global Hooks
Local hooks reside inside individual test files. These hooks do not affect the rest of the test suite.
Global hooks apply to your entire test suite. You can only define them in a JavaScript configuration file.
Test and fixture hooks can be local or global. Test run hooks are only global because they affect the entirety of your test suite.
Hooks before and after test runs
Each unique launch of TestCafe constitutes a single test run. The before
test run hook launches as soon as you launch TestCafe. TestCafe executes the after
test run hook just before the TestCafe process terminates.
Test run hooks cannot access the browser. Use test run hooks to execute server-side tasks (e.g., launch or shut down a web application).
Test run hooks are global. You can only define global hooks in a JavaScript configuration file.
const utils = require ('./my-utils.js');
module.exports = {
hooks: {
testRun: {
before: async ctx => {
ctx.serverId = 123;
utils.launchServer(ctx.serverId);
},
after: async ctx => {
utils.terminateServer(ctx.serverId);
},
},
},
};
Hooks before and after fixtures
Fixture hooks run in between tests. They cannot access the browser and execute page actions. Use fixture hooks to perform server-side operations (e.g. manage database entries).
Global fixture hooks
Global fixture hooks run before/after each of the fixtures in your test suite.
As with all global hooks, you can only declare global fixture hooks in a JavaScript configuration file.
Use CommonJs require
syntax to access Node.js modules inside your configuration file.
const utils = require ('./my-utils.js');
module.exports = {
hooks: {
fixture: {
before: async ctx => {
ctx.dbName= 'users';
utils.populateDb(ctx.dbName);
},
after: async ctx => {
utils.dropDb(ctx.dbName);
},
},
},
};
Hooks for individual fixtures
Fixture hooks run before/after an individual fixture.
Declare hooks for individual fixtures with the fixture.before and fixture.after methods.
const utils = require ('./my-utils.js');
fixture `My fixture`
.page `http://example.com`
.before( async ctx => {
ctx.dbName= 'users';
utils.populateDb(ctx.dbName);
})
.after( async ctx => {
utils.dropDb(ctx.dbName);
});
Hooks before and after tests
Hooks that run before and after tests can access the browser. Feel free to interact with the test page and include page actions in your hook.
Global test hooks
Global test hooks run before/after each of the tests in your entire test suite. They do not interfere with the execution of other test hooks.
As with all global hooks, you can only define global test hooks in a JavaScript configuration file.
Use CommonJs require
syntax to access Node.js modules inside your configuration file.
const { admin } = require('roles');
module.exports = {
hooks: {
test: {
before: async t => {
t.ctx = 'test data';
await t.useRole(admin);
},
after: async t => {
await t.click('#delete-data');
console.log(t.ctx); // > test data
}
},
},
};
Fixture-wide test hooks
Fixture-wide test hooks run before/after each of the tests in an individual fixture.
Specify these hooks with the fixture.beforeEach and fixture.afterEach methods.
Individual test hooks override fixture-wide beforeEach
and afterEach
hooks.
import { admin } from 'roles';
fixture `My fixture`
.page `http://example.com`
.beforeEach( async t => {
t.ctx = 'test data';
await t.useRole(admin);
})
.afterEach( async t => {
await t.click('#delete-data');
console.log(t.ctx); // > test data
});
Hooks for individual tests
Warning
Test-specific hooks are an emergency measure. If your test suite is well-organized, test-specific hooks offer no benefit. The only legitimate reason to use a test-specific hook is to override an existing fixture-wide hook.
TestCafe conceived hooks as a tool to help developers re-use code and maintain a cleaner codebase. Test-specific hooks do not run multiple times, and allow for the same kind of code as the test body.
Before you attach a hook to an individual test, ask yourself: “Can I move this code to the test body?”.
Test hooks run before/after individual tests. Hooks for individual tests override fixture-wide hooks of the same kind.
Specify hooks for individual tests with the test.before and test.after methods.
test
.before( async t => {
t.ctx = 'test data';
await t.useRole(admin);
})
('MyTest', async t => { /* ... */ })
.after( async t => {
await t.click('#delete-data');
console.log(t.ctx); // > test data
});
Hook Order and Priority
Global hooks do not override local test and fixture hooks. Global before
hooks precede local before
hooks. Global after
hooks follow local after
hooks.
Test-specific before
and after
hooks override fixture-wide test hooks.
The chart below shows the order in which TestCafe executes different components of the test suite.
Error Handling
If any before
hook yields an error, TestCafe skips the remainder of the current fixture and resumes execution at the fixture.after
hook.
Share Data Between Hooks and Test Code
Every test and fixture contains a unique context (ctx
) object. You can use that object to store and access data across different test entities.
Users can access an entity’s context object within the test entity itself, as well as within its hooks. If you want to share data across the entirety of your test suite, check out global user variables. Note that global user variables are read-only.
Fixture context
You can access the fixture context object within tests, test hooks, and fixture hooks.
The ctx
keyword lets you reference the object in fixture hook code.
The fixtureCtx keyword lets you reference the object in test code and test hook code.
Example 1: Local Hooks
fixture `Fixture1`
.before(async ctx => {
ctx.someProp = 123;
})
.after(async ctx => {
console.log(ctx.newProp); // > abc
});
test('Test1', async t => {
console.log(t.fixtureCtx.someProp); // > 123
});
test('Test2', async t => {
t.fixtureCtx.newProp = 'abc';
});
Example 2: Global Hooks
Configuration file (.testcaferc.js
or .testcaferc.cjs
):
module.exports = {
hooks: {
fixture: {
before: async ctx => {
ctx.someProp = 123;
ctx.newProp = 'abc';
}
}
}
};
Test file:
fixture('Fixture 1')
.before(async ctx => {
console.log(ctx); // > {someProp: 123, newProp: 'abc'}
});
test('Test 1', async t => {
console.log(t.fixtureCtx); // > { someProp: 123, newProp: 'abc' }
});
Test Context
You can access test context objects (t.ctx) within tests and their hooks.
Example 1: Local Hooks
fixture `Fixture1`
.beforeEach(async t => {
t.ctx.someProp = 123;
});
test
('Test1', async t => {
console.log(t.ctx.someProp); // > 123
})
.after(async t => {
console.log(t.ctx.someProp); // > 123
});
Example 2: Global Hooks
Configuration file (.testcaferc.js
or .testcaferc.cjs
):
module.exports = {
hooks: {
test: {
before: async t => {
t.ctx.someProp = 123;
t.ctx.newProp = 'abc';
},
after: async t => {
console.log(t.ctx); // > {someProp: 123, newProp: 'abc'}
},
},
},
};
Test file:
fixture('Fixture')
.beforeEach(async t => {
console.log(t.ctx.someProp); // > 123
});
test('Test', async t => {
console.log(t.ctx.newProp); // >abc
});
Test Run Context
Use the test run context object (ctx
) to share data between global test run hooks.
module.exports = {
hooks: {
testRun: {
before: async ctx => {
ctx.someProp = 123;
},
after: async ctx => {
console.log(ctx.someProp); // > 123
},
},
},
};
Access Fixture and Test Data in Hooks
You can access fixture and test data in hook code.
Access Fixture Data in Fixture Hooks
Fixture hooks (fixture.before
, fixture.after
) have access to the following data:
- Fixture name (
name
) - Fixture metadata (
meta
) - Fixture path (
path
)
To access fixture data, pass the info
parameter to the fixture hook alongside ctx
:
fixture `Example Fixture`
.page `http://example.com`
.meta({ fixtureMeta: 'v' })
.before( async (ctx, info) => {
const fixtureName = info.name; /* Example Fixture */
const fixtureMeta = info.meta; /* { fixtureMeta: 'v' } */
const fixturePath = info.path /* /Users/dan/testcafe/fixture.js */
});
You can reference this parameter in global fixture hooks:
module.exports = {
hooks: {
fixture: {
before: async (ctx, info) => {
const fixtureName = info.name; /* Example Fixture */
const fixtureMeta = info.meta; /* { fixtureMeta: 'v' } */
const fixturePath = info.path /* /Users/dan/testcafe/fixture.js */
}
}
}
};
Access Test and Fixture Data in Test Hooks
Test hooks (fixture.beforeEach
, fixture.afterEach
, test.before
, test.after
) have access to the following data:
- Test name
- Test metadata
- Fixture name
- Fixture metadata
- Fixture path
The t.fixture property contains fixture data. The t.test property contains test data.
fixture `Example Fixture`
.page `http://example.com`
.meta({ fixtureMeta: 'v' })
.beforeEach( async t => {
const fixtureName = t.fixture.name; /* Example Fixture */
const fixtureMeta = t.fixture.meta; /* { fixtureMeta: 'v' } */
const fixturePath = t.fixture.path /* /Users/dan/testcafe/fixture.js */
const testName = t.test.name; /* MyTest */
const testMeta = t.test.meta; /* { 'key': 'value' } */
})
test
('MyTest', async t => { /* ... */ })
.meta({ 'key': 'value' })
.after( async t => {
const fixtureName = t.fixture.name; /* Example Fixture */
const fixtureMeta = t.fixture.meta; /* { fixtureMeta: 'v' } */
const fixturePath = t.fixture.path /* /Users/dan/testcafe/fixture.js */
const testName = t.test.name; /* MyTest */
const testMeta = t.test.meta; /* { 'key': 'value' } */
});
You can reference these properties in global test hooks:
module.exports = {
hooks: {
test: {
before: async t => {
const fixtureName = t.fixture.name; /* Example Fixture */
const fixtureMeta = t.fixture.meta; /* { fixtureMeta: 'v' } */
const fixturePath = t.fixture.path /* /Users/dan/testcafe/fixture.js */
const testName = t.test.name; /* MyTest */
const testMeta = t.test.meta; /* { 'key': 'value' } */
}
},
},
};