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
Robust error and exception handling capabilities for managing uncaught errors, manual failure reporting, and debugging during test execution.
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);
});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");
});
});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();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);
});
});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");
});
});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);
});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