CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-qunit

The powerful, easy-to-use testing framework for JavaScript applications

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

hooks.mddocs/

Hook System

Global and module-level hooks for setup and teardown operations, providing flexible test lifecycle management across all tests or within specific modules.

Capabilities

Global Hooks

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}`);
});

Module-Level Hooks

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 hook

Hook Execution Order

Understanding the order of hook execution is important for proper test setup:

Setup Order (before test):

  1. Module before() (once per module, before first test)
  2. Global QUnit.hooks.beforeEach()
  3. Module beforeEach()
  4. Test execution

Teardown Order (after test):

  1. Module afterEach() (in reverse order of registration)
  2. Global QUnit.hooks.afterEach()
  3. Module 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");
  });
});

Hook Context and Scope

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 testEnvironment

Usage 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");
  });
});

Async Hooks

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();
});

Error Handling in Hooks

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
});

Hook Function Signatures

/**
 * 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
 */

Hook Registration Methods

// 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

docs

assertions.md

cli.md

configuration.md

error-handling.md

events.md

hooks.md

index.md

test-definition.md

test-flavors.md

utilities.md

tile.json