CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jest-util

Collection of utility functions for Jest testing framework

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Enhanced error handling with custom stack trace management for better test debugging. The ErrorWithStack class provides improved error reporting by excluding specific functions from stack traces and optimizing stack trace capture.

Capabilities

ErrorWithStack Class

Error class that captures stack traces while excluding a specific callsite function, providing cleaner and more useful stack traces for debugging.

/**
 * Error class with custom stack trace callsite exclusion
 */
class ErrorWithStack extends Error {
  /**
   * Creates an Error with enhanced stack trace handling
   * @param message - Error message (can be undefined)
   * @param callsite - Function to exclude from stack trace
   * @param stackLimit - Optional stack trace limit for performance
   */
  constructor(
    message: string | undefined,
    callsite: (...args: Array<any>) => unknown,
    stackLimit?: number
  );
}

Usage Examples:

import { ErrorWithStack } from "jest-util";

// Basic usage - exclude current function from stack
function validateTestInput(input: any) {
  if (input == null) {
    throw new ErrorWithStack("Input cannot be null", validateTestInput);
  }
}

// The stack trace will start from the caller of validateTestInput,
// not from within validateTestInput itself

// Jest assertion helper
function expectToBeTruthy(value: any, message?: string) {
  if (!value) {
    throw new ErrorWithStack(
      message || `Expected ${value} to be truthy`,
      expectToBeTruthy
    );
  }
}

// Usage in test
test("should validate user input", () => {
  const userData = null;
  expectToBeTruthy(userData, "User data is required");
  // Stack trace points to this line, not inside expectToBeTruthy
});

// Custom test framework utilities
function createAssertion(name: string, predicate: (value: any) => boolean) {
  return function assertion(value: any) {
    if (!predicate(value)) {
      throw new ErrorWithStack(
        `Assertion failed: ${name}`,
        assertion
      );
    }
  };
}

const shouldBePositive = createAssertion("should be positive", x => x > 0);
shouldBePositive(-5); // Stack trace excludes the assertion function

// Performance optimization with stack limit
function createPerformantError(message: string, callsite: Function) {
  return new ErrorWithStack(message, callsite, 10); // Limit stack to 10 frames
}

Advanced Usage:

// Test helper with multiple levels
function deepTestHelper(value: any) {
  return intermediateHelper(value);
}

function intermediateHelper(value: any) {
  if (typeof value !== "string") {
    // Exclude the original calling function, not intermediate ones
    throw new ErrorWithStack("Expected string", deepTestHelper);
  }
}

// Custom error factory
class ValidationError extends ErrorWithStack {
  constructor(field: string, value: any, validator: Function) {
    super(
      `Validation failed for field '${field}' with value: ${value}`,
      validator
    );
    this.name = "ValidationError";
  }
}

function validateEmail(email: string) {
  if (!email.includes("@")) {
    throw new ValidationError("email", email, validateEmail);
  }
}

// Error aggregation for test suites
function collectTestErrors(tests: Array<() => void>): ErrorWithStack[] {
  const errors: ErrorWithStack[] = [];
  
  tests.forEach((test, index) => {
    try {
      test();
    } catch (error) {
      if (error instanceof ErrorWithStack) {
        errors.push(error);
      } else {
        errors.push(new ErrorWithStack(
          `Test ${index} failed: ${error.message}`,
          collectTestErrors
        ));
      }
    }
  });
  
  return errors;
}

Stack Trace Behavior:

// Without ErrorWithStack
function regularError() {
  throw new Error("Regular error");
}

function callRegularError() {
  regularError(); // This will show in stack trace
}

// Stack trace shows:
// Error: Regular error
//     at regularError (file.js:2:9)
//     at callRegularError (file.js:6:3)
//     at ...

// With ErrorWithStack
function betterError() {
  throw new ErrorWithStack("Better error", betterError);
}

function callBetterError() {
  betterError(); // ErrorWithStack excludes betterError from trace
}

// Stack trace shows:
// ErrorWithStack: Better error
//     at callBetterError (file.js:16:3)
//     at ...
// Notice betterError function is excluded

Performance Features:

  • Stack Limit Control: Optional stackLimit parameter prevents excessive stack capture
  • Efficient Capture: Uses Error.captureStackTrace() when available (V8 engines)
  • Temporary Limit Adjustment: Temporarily increases Error.stackTraceLimit during capture for better traces
  • Memory Optimization: Limits stack depth to prevent memory issues in deep call stacks

Integration with Testing Frameworks:

// Jest custom matcher
expect.extend({
  toBeValidUser(received) {
    if (!received || !received.id || !received.name) {
      throw new ErrorWithStack(
        `Expected valid user object, received: ${JSON.stringify(received)}`,
        this.toBeValidUser
      );
    }
    return { pass: true, message: () => "User is valid" };
  }
});

// Chai-style assertion
function expect(value: any) {
  return {
    toBe(expected: any) {
      if (value !== expected) {
        throw new ErrorWithStack(
          `Expected ${value} to be ${expected}`,
          this.toBe
        );
      }
    }
  };
}

Error Message Best Practices

When using ErrorWithStack, follow these patterns for clear error messages:

  • Be Specific: Include actual and expected values when possible
  • Provide Context: Mention what operation was being performed
  • Use Consistent Format: Follow a standard message pattern across your tests
  • Include Debugging Info: Add relevant details that help identify the issue
// Good error messages
throw new ErrorWithStack(
  `Expected array to have length 3, but got length ${arr.length}`,
  validateArrayLength
);

throw new ErrorWithStack(
  `User validation failed: missing required field 'email'`,
  validateUser
);

// Less helpful messages
throw new ErrorWithStack("Invalid input", someFunction);
throw new ErrorWithStack("Error occurred", someFunction);

docs

data-manipulation.md

error-handling.md

file-system.md

garbage-collection.md

global-environment.md

index.md

module-loading.md

string-path.md

terminal.md

type-checking.md

tile.json