0
# Hook System
1
2
Global and module-level hooks for setup and teardown operations, providing flexible test lifecycle management across all tests or within specific modules.
3
4
## Capabilities
5
6
### Global Hooks
7
8
Execute functions before or after every test across all modules.
9
10
```javascript { .api }
11
/**
12
* Global beforeEach hook - runs before every test
13
* @param {Function} callback - Function to execute before each test
14
*/
15
QUnit.hooks.beforeEach(callback)
16
17
/**
18
* Global afterEach hook - runs after every test
19
* @param {Function} callback - Function to execute after each test
20
*/
21
QUnit.hooks.afterEach(callback)
22
```
23
24
**Usage Examples:**
25
26
```javascript
27
import QUnit from "qunit";
28
29
// Global setup that runs before every test
30
QUnit.hooks.beforeEach(function(assert) {
31
// Reset global state
32
window.testData = {};
33
34
// Clear any timers
35
clearAllTimers();
36
37
// Log test start
38
console.log(`Starting test: ${assert.test.testName}`);
39
});
40
41
// Global cleanup that runs after every test
42
QUnit.hooks.afterEach(function(assert) {
43
// Cleanup DOM modifications
44
document.body.innerHTML = "";
45
46
// Reset mocks
47
jest.restoreAllMocks();
48
49
// Log test completion
50
console.log(`Completed test: ${assert.test.testName}`);
51
});
52
```
53
54
### Module-Level Hooks
55
56
Execute functions at specific points in module lifecycle (documented in test-definition.md for reference).
57
58
```javascript { .api }
59
/**
60
* Module hooks (defined within module options or callback)
61
* @typedef {Object} ModuleOptions
62
* @property {Function} [before] - Run once before all tests in module
63
* @property {Function} [beforeEach] - Run before each test in module
64
* @property {Function} [afterEach] - Run after each test in module
65
* @property {Function} [after] - Run once after all tests in module
66
*/
67
68
// Available within module callback function:
69
hooks.before(callback) // Module-level before hook
70
hooks.beforeEach(callback) // Module-level beforeEach hook
71
hooks.afterEach(callback) // Module-level afterEach hook
72
hooks.after(callback) // Module-level after hook
73
```
74
75
### Hook Execution Order
76
77
Understanding the order of hook execution is important for proper test setup:
78
79
**Setup Order (before test):**
80
1. Module `before()` (once per module, before first test)
81
2. Global `QUnit.hooks.beforeEach()`
82
3. Module `beforeEach()`
83
4. Test execution
84
85
**Teardown Order (after test):**
86
1. Module `afterEach()` (in reverse order of registration)
87
2. Global `QUnit.hooks.afterEach()`
88
3. Module `after()` (once per module, after last test, in reverse order)
89
90
**Usage Examples:**
91
92
```javascript
93
// Global hooks that apply to all tests
94
QUnit.hooks.beforeEach(function(assert) {
95
// This runs before every single test
96
this.startTime = Date.now();
97
console.log(`Starting test: ${assert.test.testName}`);
98
});
99
100
QUnit.hooks.afterEach(function(assert) {
101
// This runs after every single test
102
const duration = Date.now() - this.startTime;
103
console.log(`Test duration: ${duration}ms`);
104
});
105
106
// Module with hooks defined in options
107
QUnit.module("Database Tests", {
108
before: function() {
109
// Runs once before all tests in this module
110
this.db = new TestDatabase();
111
this.db.connect();
112
},
113
114
beforeEach: function(assert) {
115
// Runs before each test in this module (after global beforeEach)
116
this.db.clearTables();
117
this.transaction = this.db.beginTransaction();
118
},
119
120
afterEach: function(assert) {
121
// Runs after each test in this module (before global afterEach)
122
this.transaction.rollback();
123
},
124
125
after: function() {
126
// Runs once after all tests in this module
127
this.db.disconnect();
128
}
129
}, function() {
130
// Tests go here
131
});
132
133
// Alternative: Module with hooks defined in callback
134
QUnit.module("API Tests", function(hooks) {
135
136
QUnit.test("user creation", function(assert) {
137
// Global beforeEach runs first
138
// Module before ran once at start
139
// Module beforeEach runs
140
// Test executes
141
// Module afterEach runs
142
// Global afterEach runs last
143
144
const user = this.db.createUser("test");
145
assert.ok(user.id, "user has ID");
146
});
147
148
QUnit.test("user deletion", function(assert) {
149
// Same hook order applies
150
const user = this.db.createUser("test");
151
this.db.deleteUser(user.id);
152
assert.notOk(this.db.findUser(user.id), "user was deleted");
153
});
154
});
155
```
156
157
### Hook Context and Scope
158
159
Hooks share context with tests and other hooks within the same module.
160
161
```javascript { .api }
162
/**
163
* Hook context - hooks and tests share the same testEnvironment object
164
* Properties can be added dynamically and will be available to all
165
* hooks and tests within the same module
166
*/
167
// this.propertyName is available in hooks and tests
168
// The 'this' context is the module's testEnvironment
169
```
170
171
**Usage Examples:**
172
173
```javascript
174
QUnit.module("API Tests", {
175
before: function() {
176
// Set up shared resources
177
this.apiClient = new APIClient();
178
this.testUser = { name: "Test User", email: "test@example.com" };
179
},
180
181
beforeEach: function() {
182
// Prepare for each test
183
this.requestId = Math.random().toString(36);
184
},
185
186
afterEach: function() {
187
// Clean up after each test
188
if (this.createdResources) {
189
this.createdResources.forEach(resource => {
190
this.apiClient.delete(resource.url);
191
});
192
this.createdResources = [];
193
}
194
}
195
}, function() {
196
197
QUnit.test("create user", function(assert) {
198
// Access shared context from hooks
199
const response = this.apiClient.post("/users", this.testUser);
200
201
// Store for cleanup
202
this.createdResources = [{ url: `/users/${response.id}` }];
203
204
assert.ok(response.id, "user was created");
205
});
206
});
207
```
208
209
### Async Hooks
210
211
Hooks can be asynchronous using promises or async/await.
212
213
```javascript { .api }
214
/**
215
* Async hooks - QUnit automatically waits for Promise resolution
216
* Both promise-returning and async/await patterns are supported
217
*/
218
219
// Promise-based hooks
220
hooks.before(function() {
221
return fetch('/setup').then(response => {
222
this.setupData = response.data;
223
});
224
});
225
226
// Async/await hooks
227
hooks.beforeEach(async function() {
228
this.testData = await generateTestData();
229
});
230
```
231
232
**Usage Examples:**
233
234
```javascript
235
QUnit.module("Async Setup Tests", {
236
before: async function() {
237
// Async setup
238
this.server = await startTestServer();
239
this.database = await connectToDatabase();
240
},
241
242
beforeEach: async function() {
243
// Async preparation for each test
244
await this.database.seedTestData();
245
this.session = await this.server.createSession();
246
},
247
248
afterEach: async function() {
249
// Async cleanup after each test
250
await this.session.destroy();
251
await this.database.clearTestData();
252
},
253
254
after: async function() {
255
// Async teardown
256
await this.database.disconnect();
257
await this.server.stop();
258
}
259
}, function() {
260
261
QUnit.test("async test with async hooks", async function(assert) {
262
const response = await this.server.request("/api/data");
263
assert.strictEqual(response.status, 200);
264
});
265
});
266
267
// Global async hooks
268
QUnit.hooks.beforeEach(async function() {
269
await setupGlobalState();
270
});
271
272
QUnit.hooks.afterEach(async function() {
273
await cleanupGlobalState();
274
});
275
```
276
277
### Error Handling in Hooks
278
279
Handle errors gracefully in hook functions.
280
281
**Usage Examples:**
282
283
```javascript
284
QUnit.module("Error Handling", {
285
before: function() {
286
try {
287
this.resource = createExpensiveResource();
288
} catch (error) {
289
// Hook errors will fail all tests in the module
290
throw new Error(`Failed to initialize resource: ${error.message}`);
291
}
292
},
293
294
beforeEach: function(assert) {
295
if (!this.resource) {
296
assert.pushResult({
297
result: false,
298
message: "Resource not available, skipping test",
299
source: "beforeEach hook"
300
});
301
return;
302
}
303
304
this.resource.reset();
305
}
306
}, function() {
307
// Tests will only run if hooks succeed
308
});
309
```
310
311
## Hook Function Signatures
312
313
```javascript { .api }
314
/**
315
* Module-level hook signatures
316
* @param {Function} before - function() - no assert parameter
317
* @param {Function} beforeEach - function(assert) - receives assert object
318
* @param {Function} afterEach - function(assert) - receives assert object
319
* @param {Function} after - function() - no assert parameter
320
*/
321
322
/**
323
* Global hook signatures
324
* @param {Function} callback - function(assert) - receives assert object
325
*/
326
QUnit.hooks.beforeEach(function(assert) {
327
// Global beforeEach always receives assert parameter
328
});
329
330
QUnit.hooks.afterEach(function(assert) {
331
// Global afterEach always receives assert parameter
332
});
333
334
/**
335
* Hook context (this)
336
* - All hooks and tests within a module share the same testEnvironment
337
* - Properties set on 'this' are available to subsequent hooks and tests
338
* - The context is reset for each module
339
*/
340
```
341
342
## Hook Registration Methods
343
344
```javascript { .api }
345
// Method 1: Module options object
346
QUnit.module('Module Name', {
347
before() { /* ... */ },
348
beforeEach(assert) { /* ... */ },
349
afterEach(assert) { /* ... */ },
350
after() { /* ... */ }
351
}, function() {
352
// tests
353
});
354
355
// Method 2: Hooks parameter in module callback
356
QUnit.module('Module Name', function(hooks) {
357
hooks.before(function() { /* ... */ });
358
hooks.beforeEach(function(assert) { /* ... */ });
359
hooks.afterEach(function(assert) { /* ... */ });
360
hooks.after(function() { /* ... */ });
361
362
// tests
363
});
364
365
// Method 3: Global hooks
366
QUnit.hooks.beforeEach(function(assert) { /* ... */ });
367
QUnit.hooks.afterEach(function(assert) { /* ... */ });
368
```