or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cli-interface.mdconfiguration.mdexception-execution.mdindex.mdtest-assertions.mdtest-control.mdtest-creation.md
tile.json

test-control.mddocs/

Test Control and Utilities

Test lifecycle management, subtests, planning, timeouts, and utility functions.

Capabilities

Test Planning

Control the expected number of assertions for deterministic test completion.

/**
 * Set the number of assertions expected in this test
 * @param n - Number of assertions expected (must be positive integer)
 */
plan(n: number): void;

Usage Examples:

test("planned test", (t) => {
  t.plan(3);
  
  t.ok(true, "first assertion");
  t.is(2 + 2, 4, "second assertion");
  t.pass("third assertion");
  
  // Test automatically ends when plan is reached
});

test("async planned test", async (t) => {
  t.plan(2);
  
  const result = await someAsyncOperation();
  t.ok(result, "async operation succeeded");
  
  const data = await anotherAsyncOperation();
  t.is(data.length, 3, "data has expected length");
  
  // No need to call t.end() - plan completion ends the test
});

Test Ending

Explicitly end tests, especially useful in inverted mode.

/**
 * Explicitly end the test (required for inverted tests without plan)
 */
end(): void;

Usage Examples:

// Inverted test requires explicit end
const t = test("inverted test");
t.is(1 + 1, 2);
t.ok(true);
t.end(); // Required

// Callback test with early end
test("early end", (t) => {
  if (process.env.SKIP_TEST) {
    t.pass("test skipped");
    t.end(); // End early
    return;
  }
  
  // Continue with normal test
  t.is(2 + 2, 4);
});

Subtests

Create nested tests within a parent test for organized test hierarchies.

/**
 * Create a subtest within the current test
 * @param name - Optional subtest name
 * @param options - Optional test configuration
 * @param callback - Optional test function for callback mode
 * @returns Test instance (inverted mode) or Promise<boolean> (callback mode)
 */
test(name?: string, options?: TestOptions, callback?: (t: Test) => void): Test | Promise<boolean>;

/**
 * Create a stealth subtest (minimal output)
 * @param name - Optional subtest name
 * @param options - Optional test configuration
 * @param callback - Optional test function for callback mode
 * @returns Test instance (inverted mode) or Promise<boolean> (callback mode)
 */
stealth(name?: string, options?: TestOptions, callback?: (t: Test) => void): Test | Promise<boolean>;

Usage Examples:

test("user management", async (t) => {
  // Setup
  const user = { name: "Alice", age: 30 };
  
  // Subtest for user creation
  await t.test("create user", (st) => {
    st.is(user.name, "Alice", "user has correct name");
    st.is(user.age, 30, "user has correct age");
  });
  
  // Subtest for user validation
  await t.test("validate user", (st) => {
    st.ok(user.name.length > 0, "user name is not empty");
    st.ok(user.age >= 0, "user age is non-negative");
  });
  
  // Stealth subtest (minimal output)
  await t.stealth("internal checks", (st) => {
    st.ok(typeof user === "object");
    st.ok(user.hasOwnProperty("name"));
  });
  
  t.pass("all user tests completed");
});

Test Timeouts

Set timeouts for individual tests or clear existing timeouts.

/**
 * Set a timeout for the current test
 * @param ms - Timeout in milliseconds, or 0 to clear timeout
 */
timeout(ms: number): void;

Usage Examples:

test("quick test", (t) => {
  t.timeout(1000); // 1 second timeout
  
  // Test must complete within 1 second
  t.is(1, 1);
});

test("long running test", async (t) => {
  t.timeout(30000); // 30 second timeout
  
  await longRunningOperation();
  t.pass("operation completed");
});

test("no timeout test", (t) => {
  t.timeout(0); // Clear any default timeout
  
  // Test can run indefinitely
  doSomethingThatMightTakeAWhile();
  t.pass("completed");
});

Teardown Handlers

Register cleanup functions that run after the test completes.

/**
 * Register a teardown function to run after test completion
 * @param fn - Cleanup function to execute
 * @param options - Optional teardown configuration
 */
teardown(fn: Function, options?: TeardownOptions): void;

interface TeardownOptions {
  /** Execution order (lower numbers run first, default: 0) */
  order?: number;
  /** Run even if test fails (default: false) */
  force?: boolean;
}

Usage Examples:

test("resource management", async (t) => {
  const resource = await createResource();
  
  // Basic teardown
  t.teardown(() => {
    resource.cleanup();
  });
  
  // Ordered teardown
  t.teardown(() => {
    console.log("cleanup 1");
  }, { order: 1 });
  
  t.teardown(() => {
    console.log("cleanup 2");
  }, { order: 0 }); // Runs first
  
  // Force teardown (runs even on test failure)
  t.teardown(() => {
    criticalCleanup();
  }, { force: true });
  
  // Use the resource
  t.ok(resource.isValid(), "resource is valid");
});

Comments

Add comments to test output for debugging and documentation.

/**
 * Add a comment to the test output
 * @param message - Comment message parts
 */
comment(...message: any[]): void;

Usage Examples:

test("debugging example", (t) => {
  t.comment("Starting test with debug info");
  
  const data = processData();
  t.comment("Processed data:", data);
  
  t.is(data.length, 3, "data has expected length");
  
  t.comment("Test completed successfully");
});

Temporary Directories

Create temporary directories for test file operations.

/**
 * Create a temporary directory for this test
 * @returns Promise<string> - Path to the temporary directory
 */
tmp(): Promise<string>;

Usage Examples:

import fs from "fs/promises";
import path from "path";

test("file operations", async (t) => {
  const tmpDir = await t.tmp();
  t.comment("Using temp directory:", tmpDir);
  
  // Create test file
  const filePath = path.join(tmpDir, "test.txt");
  await fs.writeFile(filePath, "hello world");
  
  // Verify file
  const content = await fs.readFile(filePath, "utf8");
  t.is(content, "hello world", "file content matches");
  
  // Directory is automatically cleaned up after test
});

Test Properties

Access test state and statistics during execution.

interface Test {
  /** Test name */
  readonly name: string;
  /** Number of passed assertions */
  readonly passes: number;
  /** Number of failed assertions */
  readonly fails: number;
  /** Total number of assertions */
  readonly assertions: number;
}

Usage Examples:

test("test statistics", (t) => {
  t.comment("Test name:", t.name);
  t.comment("Initial stats - passes:", t.passes, "fails:", t.fails);
  
  t.pass("first assertion");
  t.is(2 + 2, 4, "second assertion");
  
  t.comment("Current stats - passes:", t.passes, "fails:", t.fails);
  t.is(t.assertions, 2, "total assertions count");
  
  // Use stats for conditional logic
  if (t.fails === 0) {
    t.comment("All assertions passing so far");
  }
});

Integration Patterns

These control utilities work together for comprehensive test management:

test("comprehensive test example", async (t) => {
  t.plan(4);
  t.timeout(5000);
  t.comment("Starting comprehensive test");
  
  const tmpDir = await t.tmp();
  const resource = await setupResource(tmpDir);
  
  t.teardown(async () => {
    await resource.cleanup();
    t.comment("Resource cleaned up");
  });
  
  // Subtests count toward parent plan
  await t.test("resource creation", (st) => {
    st.ok(resource.isValid());
  }); // Counts as 1 assertion
  
  t.is(resource.type, "test", "resource type"); // 2
  t.ok(resource.ready, "resource ready"); // 3
  t.pass("test completed"); // 4 - plan complete
});