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

error-handling.mddocs/

Error Handling

Robust error and exception handling capabilities for managing uncaught errors, manual failure reporting, and debugging during test execution.

Capabilities

Uncaught Error Handling

Handle and report uncaught errors that occur during test execution.

/**
 * Register callback for uncaught window errors (browser environment)
 * @param {Function} callback - Function to handle window errors
 */
QUnit.onError(callback)

/**
 * Register callback for uncaught exceptions (Node.js and modern browsers)
 * @param {Function} callback - Function to handle uncaught exceptions
 */
QUnit.onUncaughtException(callback)

Usage Examples:

import QUnit from "qunit";

// Handle uncaught window errors in browser
QUnit.onError(function(errorEvent) {
  console.error("Uncaught window error:", {
    message: errorEvent.message,
    filename: errorEvent.filename,
    lineno: errorEvent.lineno,
    colno: errorEvent.colno,
    error: errorEvent.error
  });
  
  // Return true to prevent default browser error handling
  return true;
});

// Handle uncaught exceptions (promises, async functions, etc.)
QUnit.onUncaughtException(function(error) {
  console.error("Uncaught exception:", {
    name: error.name,
    message: error.message,
    stack: error.stack
  });
  
  // Log additional context if available
  if (QUnit.config.current) {
    console.error("Error occurred in test:", QUnit.config.current.testName);
  }
});

// Example test that might generate uncaught errors
QUnit.test("async error handling", function(assert) {
  const done = assert.async();
  
  // This promise rejection might be uncaught
  Promise.resolve().then(() => {
    throw new Error("Simulated async error");
  });
  
  setTimeout(() => {
    assert.ok(true, "test completed");
    done();
  }, 100);
});

Manual Failure Reporting

Manually report test failures and push custom failure messages.

/**
 * Manually record a test failure with custom message and source location
 * @param message - Failure message
 * @param source - Source location information (optional)
 */
function pushFailure(message: string, source?: string): void;

Usage Examples:

QUnit.test("manual failure reporting", function(assert) {
  const user = getCurrentUser();
  
  if (!user) {
    // Report failure when user is not available
    QUnit.pushFailure("Test requires authenticated user but none found");
    return;
  }
  
  // Test continues if user is available
  assert.ok(user.id, "user has ID");
});

// Helper function for conditional testing
function requireFeature(featureName, callback) {
  if (!isFeatureEnabled(featureName)) {
    QUnit.pushFailure(
      `Test requires feature '${featureName}' to be enabled`,
      QUnit.stack(1) // Include stack trace from caller
    );
    return;
  }
  
  callback();
}

QUnit.test("feature-dependent test", function(assert) {
  requireFeature("advanced-search", () => {
    const results = performAdvancedSearch("query");
    assert.ok(results.length > 0, "search returns results");
  });
});

Error Context and Debugging

Enhanced error reporting with context information and debugging utilities.

Usage Examples:

// Enhanced error reporting with context
function createTestErrorHandler() {
  const testContext = {
    browser: navigator?.userAgent || "Node.js",
    url: window?.location?.href || "N/A",
    timestamp: new Date().toISOString()
  };
  
  QUnit.onError(function(errorEvent) {
    const errorInfo = {
      ...testContext,
      error: {
        message: errorEvent.message,
        filename: errorEvent.filename,
        line: errorEvent.lineno,
        column: errorEvent.colno,
        stack: errorEvent.error?.stack
      },
      test: QUnit.config.current ? {
        name: QUnit.config.current.testName,
        module: QUnit.config.current.module.name
      } : null
    };
    
    // Send to error tracking service
    console.error("Test execution error:", JSON.stringify(errorInfo, null, 2));
    
    // Report as test failure
    QUnit.pushFailure(
      `Uncaught error in test: ${errorEvent.message}`,
      errorEvent.error?.stack
    );
  });
  
  QUnit.onUncaughtException(function(error) {
    const errorInfo = {
      ...testContext,
      error: {
        name: error.name,
        message: error.message,
        stack: error.stack
      },
      test: QUnit.config.current ? {
        name: QUnit.config.current.testName,
        module: QUnit.config.current.module.name
      } : null
    };
    
    console.error("Uncaught exception in test:", JSON.stringify(errorInfo, null, 2));
    
    QUnit.pushFailure(
      `Uncaught exception: ${error.name}: ${error.message}`,
      error.stack
    );
  });
}

// Initialize error handling
createTestErrorHandler();

Test-Specific Error Handling

Handle errors within specific tests or modules.

Usage Examples:

QUnit.module("Error Handling Tests", {
  beforeEach: function() {
    // Set up test-specific error tracking
    this.errors = [];
    
    this.originalOnError = QUnit.onError;
    QUnit.onError = (error) => {
      this.errors.push(error);
      return true; // Prevent default handling
    };
  },
  
  afterEach: function() {
    // Restore original error handler
    QUnit.onError = this.originalOnError;
  }
}, function() {
  
  QUnit.test("capture and verify errors", function(assert) {
    // Intentionally trigger an error
    setTimeout(() => {
      throw new Error("Test error");
    }, 10);
    
    const done = assert.async();
    
    setTimeout(() => {
      assert.strictEqual(this.errors.length, 1, "one error was captured");
      assert.strictEqual(this.errors[0].message, "Test error", "correct error message");
      done();
    }, 50);
  });
});

Error Recovery and Resilience

Implement error recovery patterns for robust test suites.

Usage Examples:

// Retry mechanism for flaky tests
function retryOnError(testFn, maxRetries = 3) {
  return function(assert) {
    let attempt = 0;
    let lastError = null;
    
    const runTest = () => {
      attempt++;
      
      try {
        const result = testFn.call(this, assert);
        
        // Handle async tests
        if (result && typeof result.catch === 'function') {
          return result.catch(error => {
            lastError = error;
            if (attempt < maxRetries) {
              console.warn(`Test attempt ${attempt} failed, retrying...`);
              return runTest();
            } else {
              QUnit.pushFailure(
                `Test failed after ${maxRetries} attempts. Last error: ${error.message}`,
                error.stack
              );
            }
          });
        }
        
        return result;
      } catch (error) {
        lastError = error;
        if (attempt < maxRetries) {
          console.warn(`Test attempt ${attempt} failed, retrying...`);
          return runTest();
        } else {
          QUnit.pushFailure(
            `Test failed after ${maxRetries} attempts. Last error: ${error.message}`,
            error.stack
          );
        }
      }
    };
    
    return runTest();
  };
}

QUnit.test("flaky network test", retryOnError(async function(assert) {
  const response = await fetch("/api/unstable-endpoint");
  assert.strictEqual(response.status, 200, "API responds successfully");
}));

// Circuit breaker pattern for external dependencies
class TestCircuitBreaker {
  constructor(threshold = 3, timeout = 30000) {
    this.failureCount = 0;
    this.threshold = threshold;
    this.timeout = timeout;
    this.nextAttempt = Date.now();
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
  }
  
  async execute(testFn) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        QUnit.pushFailure("Circuit breaker is OPEN - skipping test");
        return;
      } else {
        this.state = 'HALF_OPEN';
      }
    }
    
    try {
      const result = await testFn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
  }
  
  onFailure() {
    this.failureCount++;
    if (this.failureCount >= this.threshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.timeout;
    }
  }
}

const externalServiceBreaker = new TestCircuitBreaker();

QUnit.test("external service test", async function(assert) {
  await externalServiceBreaker.execute(async () => {
    const result = await callExternalService();
    assert.ok(result.success, "external service responded");
  });
});

Legacy Error Handling

Support for deprecated error handling methods.

/**
 * @deprecated Use QUnit.onUncaughtException instead
 * Handle unhandled promise rejections
 * @param reason - Rejection reason
 */
function onUnhandledRejection(reason: any): void;

Usage Examples:

// Legacy method (deprecated but still available)
QUnit.onUnhandledRejection(function(reason) {
  console.warn("QUnit.onUnhandledRejection is deprecated");
  console.error("Unhandled rejection:", reason);
});

// Modern equivalent
QUnit.onUncaughtException(function(error) {
  console.error("Uncaught exception:", error);
});

Types

interface ErrorHandler {
  (error: Error): void;
}

interface WindowErrorHandler {
  (errorEvent: ErrorEvent): boolean | void;
}

interface ErrorEvent {
  /** Error message */
  message: string;
  /** Source filename */
  filename: string;
  /** Line number */
  lineno: number;
  /** Column number */
  colno: number;
  /** Error object */
  error: Error;
}

interface TestError {
  /** Error name */
  name: string;
  /** Error message */
  message: string;
  /** Stack trace */
  stack: string;
  /** Source location */
  source?: string;
  /** Test context when error occurred */
  testName?: string;
  /** Module context when error occurred */
  module?: string;
}

interface ErrorContext {
  /** Current test information */
  test?: {
    name: string;
    module: string;
    id: string;
  };
  /** Browser/environment information */
  environment?: {
    userAgent: string;
    url: string;
    timestamp: string;
  };
  /** Custom error data */
  [key: string]: any;
}

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