Step 00: Basic Test Function
In this step, we implement the most basic test function test() with async/await support.
Goals
- Register test cases with
test(name, fn) - Provide
it(name, fn)as an alias fortest - Support both synchronous and asynchronous tests
- Run tests and display results with
runTests()
Implementation
src/index.ts
typescript
export type TestFunction = () => void | Promise<void>;
interface TestCase {
name: string;
fn: TestFunction;
}
export interface TestResult {
name: string;
status: "passed" | "failed";
error?: Error;
}
export interface TestSummary {
total: number;
passed: number;
failed: number;
results: TestResult[];
}
const testCases: TestCase[] = [];
export function test(name: string, fn: TestFunction): void {
testCases.push({ name, fn });
}
export const it = test;
export async function runTests(): Promise<TestSummary> {
const results: TestResult[] = [];
for (const testCase of testCases) {
try {
await testCase.fn();
results.push({ name: testCase.name, status: "passed" });
console.log(` ✓ ${testCase.name}`);
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
results.push({ name: testCase.name, status: "failed", error: err });
console.log(` ✗ ${testCase.name}`);
console.log(` Error: ${err.message}`);
}
}
const passed = results.filter((r) => r.status === "passed").length;
const failed = results.filter((r) => r.status === "failed").length;
console.log("");
console.log(`Tests: ${passed} passed, ${failed} failed, ${results.length} total`);
return { total: results.length, passed, failed, results };
}
export function clearTests(): void {
testCases.length = 0;
}Usage Example
typescript
import { test, it, runTests } from "../src/index.js";
test("should pass a simple test", () => {
const result = 1 + 1;
if (result !== 2) {
throw new Error(`Expected 2 but got ${result}`);
}
});
it("should work with it alias", () => {
const arr = [1, 2, 3];
if (arr.length !== 3) {
throw new Error("Array length should be 3");
}
});
test("should handle async tests", async () => {
const result = await Promise.resolve(42);
if (result !== 42) {
throw new Error(`Expected 42 but got ${result}`);
}
});
test("should fail intentionally", () => {
throw new Error("This test is expected to fail");
});
runTests().then((summary) => {
if (summary.failed > 0) {
process.exit(1);
}
});Output Example
✓ should pass a simple test
✓ should work with it alias
✓ should handle async tests
✗ should fail intentionally
Error: This test is expected to fail
Tests: 3 passed, 1 failed, 4 totalExported API
| API | Description |
|---|---|
test(name, fn) | Register a test case |
it(name, fn) | Alias for test |
runTests() | Run tests and return results |
clearTests() | Clear all registered test cases |
How to Run
bash
cd impls/00-basic-test-function
bun run exampleOr:
bash
pnpm example:00Key Concepts
Test Case Collection
The test() function simply registers test cases into a global array. This allows tests to be automatically collected when the file is loaded.
Async/Await Support
By using await testCase.fn() inside runTests(), both synchronous and asynchronous test functions are handled transparently.
Error Handling
When an exception is thrown inside a test, that test is marked as failed. At this stage, we use simple throw new Error() for assertions, but we'll introduce expect() in the next step.