The powerful, easy-to-use testing framework for JavaScript applications
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Global and module-level hooks for setup and teardown operations, providing flexible test lifecycle management across all tests or within specific modules.
Execute functions before or after every test across all modules.
/**
* Global beforeEach hook - runs before every test
* @param {Function} callback - Function to execute before each test
*/
QUnit.hooks.beforeEach(callback)
/**
* Global afterEach hook - runs after every test
* @param {Function} callback - Function to execute after each test
*/
QUnit.hooks.afterEach(callback)Usage Examples:
import QUnit from "qunit";
// Global setup that runs before every test
QUnit.hooks.beforeEach(function(assert) {
// Reset global state
window.testData = {};
// Clear any timers
clearAllTimers();
// Log test start
console.log(`Starting test: ${assert.test.testName}`);
});
// Global cleanup that runs after every test
QUnit.hooks.afterEach(function(assert) {
// Cleanup DOM modifications
document.body.innerHTML = "";
// Reset mocks
jest.restoreAllMocks();
// Log test completion
console.log(`Completed test: ${assert.test.testName}`);
});Execute functions at specific points in module lifecycle (documented in test-definition.md for reference).
/**
* Module hooks (defined within module options or callback)
* @typedef {Object} ModuleOptions
* @property {Function} [before] - Run once before all tests in module
* @property {Function} [beforeEach] - Run before each test in module
* @property {Function} [afterEach] - Run after each test in module
* @property {Function} [after] - Run once after all tests in module
*/
// Available within module callback function:
hooks.before(callback) // Module-level before hook
hooks.beforeEach(callback) // Module-level beforeEach hook
hooks.afterEach(callback) // Module-level afterEach hook
hooks.after(callback) // Module-level after hookUnderstanding the order of hook execution is important for proper test setup:
Setup Order (before test):
before() (once per module, before first test)QUnit.hooks.beforeEach()beforeEach()Teardown Order (after test):
afterEach() (in reverse order of registration)QUnit.hooks.afterEach()after() (once per module, after last test, in reverse order)Usage Examples:
// Global hooks that apply to all tests
QUnit.hooks.beforeEach(function(assert) {
// This runs before every single test
this.startTime = Date.now();
console.log(`Starting test: ${assert.test.testName}`);
});
QUnit.hooks.afterEach(function(assert) {
// This runs after every single test
const duration = Date.now() - this.startTime;
console.log(`Test duration: ${duration}ms`);
});
// Module with hooks defined in options
QUnit.module("Database Tests", {
before: function() {
// Runs once before all tests in this module
this.db = new TestDatabase();
this.db.connect();
},
beforeEach: function(assert) {
// Runs before each test in this module (after global beforeEach)
this.db.clearTables();
this.transaction = this.db.beginTransaction();
},
afterEach: function(assert) {
// Runs after each test in this module (before global afterEach)
this.transaction.rollback();
},
after: function() {
// Runs once after all tests in this module
this.db.disconnect();
}
}, function() {
// Tests go here
});
// Alternative: Module with hooks defined in callback
QUnit.module("API Tests", function(hooks) {
QUnit.test("user creation", function(assert) {
// Global beforeEach runs first
// Module before ran once at start
// Module beforeEach runs
// Test executes
// Module afterEach runs
// Global afterEach runs last
const user = this.db.createUser("test");
assert.ok(user.id, "user has ID");
});
QUnit.test("user deletion", function(assert) {
// Same hook order applies
const user = this.db.createUser("test");
this.db.deleteUser(user.id);
assert.notOk(this.db.findUser(user.id), "user was deleted");
});
});Hooks share context with tests and other hooks within the same module.
/**
* Hook context - hooks and tests share the same testEnvironment object
* Properties can be added dynamically and will be available to all
* hooks and tests within the same module
*/
// this.propertyName is available in hooks and tests
// The 'this' context is the module's testEnvironmentUsage Examples:
QUnit.module("API Tests", {
before: function() {
// Set up shared resources
this.apiClient = new APIClient();
this.testUser = { name: "Test User", email: "test@example.com" };
},
beforeEach: function() {
// Prepare for each test
this.requestId = Math.random().toString(36);
},
afterEach: function() {
// Clean up after each test
if (this.createdResources) {
this.createdResources.forEach(resource => {
this.apiClient.delete(resource.url);
});
this.createdResources = [];
}
}
}, function() {
QUnit.test("create user", function(assert) {
// Access shared context from hooks
const response = this.apiClient.post("/users", this.testUser);
// Store for cleanup
this.createdResources = [{ url: `/users/${response.id}` }];
assert.ok(response.id, "user was created");
});
});Hooks can be asynchronous using promises or async/await.
/**
* Async hooks - QUnit automatically waits for Promise resolution
* Both promise-returning and async/await patterns are supported
*/
// Promise-based hooks
hooks.before(function() {
return fetch('/setup').then(response => {
this.setupData = response.data;
});
});
// Async/await hooks
hooks.beforeEach(async function() {
this.testData = await generateTestData();
});Usage Examples:
QUnit.module("Async Setup Tests", {
before: async function() {
// Async setup
this.server = await startTestServer();
this.database = await connectToDatabase();
},
beforeEach: async function() {
// Async preparation for each test
await this.database.seedTestData();
this.session = await this.server.createSession();
},
afterEach: async function() {
// Async cleanup after each test
await this.session.destroy();
await this.database.clearTestData();
},
after: async function() {
// Async teardown
await this.database.disconnect();
await this.server.stop();
}
}, function() {
QUnit.test("async test with async hooks", async function(assert) {
const response = await this.server.request("/api/data");
assert.strictEqual(response.status, 200);
});
});
// Global async hooks
QUnit.hooks.beforeEach(async function() {
await setupGlobalState();
});
QUnit.hooks.afterEach(async function() {
await cleanupGlobalState();
});Handle errors gracefully in hook functions.
Usage Examples:
QUnit.module("Error Handling", {
before: function() {
try {
this.resource = createExpensiveResource();
} catch (error) {
// Hook errors will fail all tests in the module
throw new Error(`Failed to initialize resource: ${error.message}`);
}
},
beforeEach: function(assert) {
if (!this.resource) {
assert.pushResult({
result: false,
message: "Resource not available, skipping test",
source: "beforeEach hook"
});
return;
}
this.resource.reset();
}
}, function() {
// Tests will only run if hooks succeed
});/**
* Module-level hook signatures
* @param {Function} before - function() - no assert parameter
* @param {Function} beforeEach - function(assert) - receives assert object
* @param {Function} afterEach - function(assert) - receives assert object
* @param {Function} after - function() - no assert parameter
*/
/**
* Global hook signatures
* @param {Function} callback - function(assert) - receives assert object
*/
QUnit.hooks.beforeEach(function(assert) {
// Global beforeEach always receives assert parameter
});
QUnit.hooks.afterEach(function(assert) {
// Global afterEach always receives assert parameter
});
/**
* Hook context (this)
* - All hooks and tests within a module share the same testEnvironment
* - Properties set on 'this' are available to subsequent hooks and tests
* - The context is reset for each module
*/// Method 1: Module options object
QUnit.module('Module Name', {
before() { /* ... */ },
beforeEach(assert) { /* ... */ },
afterEach(assert) { /* ... */ },
after() { /* ... */ }
}, function() {
// tests
});
// Method 2: Hooks parameter in module callback
QUnit.module('Module Name', function(hooks) {
hooks.before(function() { /* ... */ });
hooks.beforeEach(function(assert) { /* ... */ });
hooks.afterEach(function(assert) { /* ... */ });
hooks.after(function() { /* ... */ });
// tests
});
// Method 3: Global hooks
QUnit.hooks.beforeEach(function(assert) { /* ... */ });
QUnit.hooks.afterEach(function(assert) { /* ... */ });Install with Tessl CLI
npx tessl i tessl/npm-qunit