or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

database.mdffi.mdfile-system.mdglobals.mdhtml-rewriter.mdhttp-server.mdindex.mdprocess-management.mdredis.mds3.mdshell.mdtesting.mdutilities.md
tile.json

testing.mddocs/

Testing Framework

Jest-compatible testing framework with built-in mocking, fake timers, comprehensive expectation matchers, and seamless TypeScript integration optimized for fast test execution.

Core Imports

import { describe, test, it, expect, beforeAll, beforeEach, afterAll, afterEach } from "bun:test";
import { mock, spyOn, jest } from "bun:test";

Capabilities

Test Organization

Structure and organize test suites with describe blocks and individual test cases.

/**
 * Group related tests together
 * @param name - Description of the test group
 * @param fn - Function containing the tests
 */
function describe(name: string, fn: () => void): void;

/**
 * Define an individual test case
 * @param name - Test description
 * @param fn - Test function
 */
function test(name: string, fn: () => void | Promise<void>): void;

/**
 * Alias for test() - Jest compatibility
 * @param name - Test description  
 * @param fn - Test function
 */
function it(name: string, fn: () => void | Promise<void>): void;

/** Skip a test group - won't be executed */
declare const describe.skip: typeof describe;
/** Run only this test group - others will be skipped */
declare const describe.only: typeof describe;
/** Mark test group as todo - will be reported but not executed */
declare const describe.todo: typeof describe;

/** Skip a test - won't be executed */
declare const test.skip: typeof test;
/** Run only this test - others will be skipped */
declare const test.only: typeof test;
/** Mark test as todo - will be reported but not executed */
declare const test.todo: typeof test;

/** Skip a test - won't be executed */
declare const it.skip: typeof it;
/** Run only this test - others will be skipped */
declare const it.only: typeof it;
/** Mark test as todo - will be reported but not executed */
declare const it.todo: typeof it;

Usage Examples:

import { describe, test, expect } from "bun:test";

describe("Math operations", () => {
  test("should add numbers correctly", () => {
    expect(2 + 2).toBe(4);
  });

  test("should multiply numbers correctly", () => {
    expect(3 * 4).toBe(12);
  });

  test.skip("this test is temporarily disabled", () => {
    // This won't run
  });
});

// Async tests
describe("Async operations", () => {
  test("should handle promises", async () => {
    const result = await Promise.resolve("success");
    expect(result).toBe("success");
  });
});

Test Lifecycle Hooks

Execute setup and teardown code before and after tests.

/**
 * Run before all tests in the current describe block
 * @param fn - Setup function
 */
function beforeAll(fn: () => void | Promise<void>): void;

/**
 * Run before each test in the current describe block
 * @param fn - Setup function
 */
function beforeEach(fn: () => void | Promise<void>): void;

/**
 * Run after all tests in the current describe block
 * @param fn - Cleanup function
 */
function afterAll(fn: () => void | Promise<void>): void;

/**
 * Run after each test in the current describe block
 * @param fn - Cleanup function
 */
function afterEach(fn: () => void | Promise<void>): void;

Usage Examples:

import { describe, test, beforeEach, afterEach } from "bun:test";

describe("Database tests", () => {
  let db: Database;

  beforeAll(async () => {
    // Setup shared resources
    db = new Database(":memory:");
  });

  beforeEach(() => {
    // Setup for each test
    db.exec("DELETE FROM users");
  });

  afterEach(() => {
    // Cleanup after each test
    console.log("Test completed");
  });

  afterAll(() => {
    // Cleanup shared resources
    db.close();
  });

  test("should insert user", () => {
    // Test implementation
  });
});

Expectations and Matchers

Comprehensive assertion library with Jest-compatible matchers and custom error messages.

/**
 * Create an expectation for testing values
 * @param actual - Value to test
 * @returns Matchers object for assertions
 */
function expect<T>(actual: T): Matchers<T>;

interface Matchers<T> {
  /** Strict equality check using Object.is() */
  toBe(expected: T): void;
  /** Deep equality check for objects and arrays */
  toEqual(expected: T): void;
  /** Check if value is strictly equal to true */
  toBeTruthy(): void;
  /** Check if value is strictly equal to false */
  toBeFalsy(): void;
  /** Check if value is null */
  toBeNull(): void;
  /** Check if value is undefined */
  toBeUndefined(): void;
  /** Check if value is defined (not undefined) */
  toBeDefined(): void;
  /** Check if value is NaN */
  toBeNaN(): void;
  /** Check if number is close to expected (for floating point) */
  toBeCloseTo(expected: number, precision?: number): void;
  /** Check if number is greater than expected */
  toBeGreaterThan(expected: number): void;
  /** Check if number is greater than or equal to expected */
  toBeGreaterThanOrEqual(expected: number): void;
  /** Check if number is less than expected */
  toBeLessThan(expected: number): void;
  /** Check if number is less than or equal to expected */
  toBeLessThanOrEqual(expected: number): void;
  /** Check if function throws an error */
  toThrow(error?: string | RegExp | Error | ErrorConstructor): void;
  /** Async version of toThrow for promises */
  toThrowError(error?: string | RegExp | Error | ErrorConstructor): void;
  /** Check if object has property */
  toHaveProperty(propertyPath: string | string[], value?: any): void;
  /** Check if array/string has specific length */
  toHaveLength(length: number): void;
  /** Check if string/array contains substring/element */
  toContain(item: any): void;
  /** Check if string matches regular expression */
  toMatch(regexp: RegExp | string): void;
  /** Check if array contains object matching properties */
  toContainEqual(expected: any): void;
  /** Check if mock function was called */
  toHaveBeenCalled(): void;
  /** Check if mock function was called specific number of times */
  toHaveBeenCalledTimes(times: number): void;
  /** Check if mock function was called with specific arguments */
  toHaveBeenCalledWith(...args: any[]): void;
  /** Check if mock function was last called with specific arguments */
  toHaveBeenLastCalledWith(...args: any[]): void;
  /** Check if mock function returned specific value */
  toHaveReturnedWith(value: any): void;
  /** Check if mock function returned (not threw) */
  toHaveReturned(): void;
  /** Check if mock function returned specific number of times */
  toHaveReturnedTimes(times: number): void;

  /** Negate the matcher */
  not: Matchers<T>;
  /** Add custom error message */
  withContext(message: string): Matchers<T>;
}

Usage Examples:

import { test, expect } from "bun:test";

test("number comparisons", () => {
  expect(2 + 2).toBe(4);
  expect(0.1 + 0.2).toBeCloseTo(0.3);
  expect(5).toBeGreaterThan(3);
  expect(10).toBeLessThanOrEqual(10);
});

test("string matching", () => {
  expect("hello world").toContain("world");
  expect("test@example.com").toMatch(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});

test("object testing", () => {
  const user = { id: 1, name: "Alice", email: "alice@example.com" };
  expect(user).toHaveProperty("name", "Alice");
  expect(user).toEqual({ id: 1, name: "Alice", email: "alice@example.com" });
});

test("array testing", () => {
  const numbers = [1, 2, 3, 4, 5];
  expect(numbers).toHaveLength(5);
  expect(numbers).toContain(3);
  expect(numbers).toContainEqual(4);
});

test("error testing", () => {
  expect(() => {
    throw new Error("Something went wrong");
  }).toThrow("Something went wrong");
  
  expect(() => JSON.parse("invalid")).toThrow(SyntaxError);
});

Mocking

Comprehensive mocking system for functions, modules, and objects with call tracking and behavior control.

/**
 * Create a mock function
 * @param implementation - Optional implementation function
 * @returns Mock function with tracking capabilities
 */
function mock<T extends (...args: any[]) => any>(implementation?: T): Mock<T>;

interface Mock<T extends (...args: any[]) => any> extends jest.MockedFunction<T> {
  /** Mock implementation function */
  mockImplementation(fn: T): Mock<T>;
  /** Mock implementation for one call */
  mockImplementationOnce(fn: T): Mock<T>;
  /** Mock return value */
  mockReturnValue(value: ReturnType<T>): Mock<T>;
  /** Mock return value for one call */
  mockReturnValueOnce(value: ReturnType<T>): Mock<T>;
  /** Mock resolved promise value */
  mockResolvedValue(value: Awaited<ReturnType<T>>): Mock<T>;
  /** Mock resolved promise value for one call */
  mockResolvedValueOnce(value: Awaited<ReturnType<T>>): Mock<T>;
  /** Mock rejected promise */
  mockRejectedValue(error: any): Mock<T>;
  /** Mock rejected promise for one call */
  mockRejectedValueOnce(error: any): Mock<T>;
  /** Clear call history but keep implementation */
  mockClear(): Mock<T>;
  /** Reset implementation to undefined */
  mockReset(): Mock<T>;
  /** Restore original implementation */
  mockRestore(): void;
  
  /** All calls made to this mock */
  mock: {
    calls: Parameters<T>[];
    results: Array<{ type: 'return' | 'throw'; value: any }>;
    instances: any[];
    contexts: any[];
    invocationCallOrder: number[];
  };
}

/**
 * Spy on an existing method
 * @param object - Object containing the method
 * @param method - Method name to spy on
 * @returns Spy that can be used to track calls and modify behavior
 */
function spyOn<T extends object, K extends keyof T>(
  object: T,
  method: K
): Mock<T[K] extends (...args: any[]) => any ? T[K] : never>;

/**
 * Mock an entire module
 * @param modulePath - Path to module to mock
 * @param factory - Factory function that returns mock implementation
 * @returns Mock module
 */
function mockModule(modulePath: string, factory: () => any): void;

Usage Examples:

import { test, expect, mock, spyOn } from "bun:test";

test("mock functions", () => {
  const mockFn = mock();
  
  mockFn("arg1", "arg2");
  mockFn("arg3");
  
  expect(mockFn).toHaveBeenCalledTimes(2);
  expect(mockFn).toHaveBeenCalledWith("arg1", "arg2");
  expect(mockFn).toHaveBeenLastCalledWith("arg3");
});

test("mock with implementation", () => {
  const add = mock((a: number, b: number) => a + b);
  
  expect(add(2, 3)).toBe(5);
  expect(add).toHaveBeenCalledWith(2, 3);
});

test("mock return values", () => {
  const mockFn = mock();
  mockFn.mockReturnValue(42);
  mockFn.mockReturnValueOnce(100);
  
  expect(mockFn()).toBe(100); // First call
  expect(mockFn()).toBe(42);  // Subsequent calls
});

test("spy on methods", () => {
  const calculator = {
    add: (a: number, b: number) => a + b,
    multiply: (a: number, b: number) => a * b
  };
  
  const addSpy = spyOn(calculator, "add");
  
  calculator.add(2, 3);
  expect(addSpy).toHaveBeenCalledWith(2, 3);
  
  addSpy.mockReturnValue(10);
  expect(calculator.add(2, 3)).toBe(10);
  
  addSpy.mockRestore();
  expect(calculator.add(2, 3)).toBe(5); // Original behavior restored
});

// Module mocking
mockModule("fs", () => ({
  readFileSync: mock(() => "mocked file content"),
  writeFileSync: mock()
}));

Time Control

Control time and timers in tests for deterministic testing of time-dependent code.

/**
 * Set the system time for tests
 * @param time - Time to set (Date, timestamp, or ISO string)
 */
function setSystemTime(time?: Date | number | string): void;

/**
 * Restore real system time
 */
function restoreSystemTime(): void;

/** Jest compatibility namespace for time control */
declare namespace jest {
  /** Enable fake timers */
  function useFakeTimers(): void;
  /** Disable fake timers and use real timers */
  function useRealTimers(): void;
  /** Advance fake timers by specified time */
  function advanceTimersByTime(msToRun: number): void;
  /** Run all pending timers */
  function runAllTimers(): void;
  /** Run only currently pending timers */
  function runOnlyPendingTimers(): void;
  /** Set system time */
  function setSystemTime(time: Date | number): void;
  /** Get number of pending timers */
  function getTimerCount(): number;
}

Usage Examples:

import { test, expect, jest, setSystemTime } from "bun:test";

test("time-dependent functionality", () => {
  jest.useFakeTimers();
  
  const callback = mock();
  setTimeout(callback, 1000);
  
  expect(callback).not.toHaveBeenCalled();
  
  jest.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalled();
  
  jest.useRealTimers();
});

test("date-dependent functionality", () => {
  const fixedDate = new Date("2023-01-01T00:00:00Z");
  setSystemTime(fixedDate);
  
  expect(new Date().getFullYear()).toBe(2023);
  
  restoreSystemTime();
});

Jest Compatibility

Full Jest compatibility layer for easy migration and familiar APIs.

declare namespace jest {
  /** Create a mock function */
  function fn<T extends (...args: any[]) => any>(implementation?: T): Mock<T>;
  /** Spy on object method */
  function spyOn<T extends object, K extends keyof T>(object: T, method: K): Mock<T[K]>;
  /** Clear all mocks */
  function clearAllMocks(): void;
  /** Reset all mocks */
  function resetAllMocks(): void;
  /** Restore all mocks */
  function restoreAllMocks(): void;
  /** Mock module */
  function mock(moduleName: string, factory?: () => any): void;
  /** Unmock module */
  function unmock(moduleName: string): void;
  /** Require actual module (bypass mocks) */
  function requireActual<T = any>(moduleName: string): T;
  /** Require mock module */
  function requireMock<T = any>(moduleName: string): T;
}

Usage Examples:

import { test, expect, jest } from "bun:test";

test("Jest compatibility", () => {
  const mockFn = jest.fn((x: number) => x * 2);
  
  expect(mockFn(5)).toBe(10);
  expect(mockFn).toHaveBeenCalledWith(5);
  
  jest.clearAllMocks();
  expect(mockFn).not.toHaveBeenCalled();
});

Type Definitions

/** Test function signature */
type TestFunction = () => void | Promise<void>;

/** Setup/teardown function signature */
type HookFunction = () => void | Promise<void>;

/** Test result metadata */
interface TestResult {
  name: string;
  status: "pass" | "fail" | "skip" | "todo";
  duration: number;
  error?: Error;
}

/** Test suite metadata */
interface SuiteResult {
  name: string;
  tests: TestResult[];
  suites: SuiteResult[];
}