Skip to content

Step 00: Basic Test Function

このステップでは、最も基本的なテスト関数 test() を実装します。async/await にも対応しています。

目標

  • test(name, fn) 関数でテストケースを登録できる
  • it(name, fn)test のエイリアスとして提供
  • 同期・非同期の両方のテストをサポート
  • runTests() でテストを実行し結果を表示

実装

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;
}

使用例

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);
  }
});

出力例

  ✓ 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 total

エクスポート API

API説明
test(name, fn)テストケースを登録
it(name, fn)test のエイリアス
runTests()テストを実行し結果を返す
clearTests()テストケースをクリア

実行方法

bash
cd impls/00-basic-test-function
bun run example

または:

bash
pnpm example:00

ポイント解説

テストケースの収集

test() 関数は、テストケースをグローバルな配列に登録するだけです。これにより、ファイルが読み込まれた時点でテストが自動的に収集されます。

async/await 対応

runTests() 内で await testCase.fn() を使用することで、同期・非同期の両方のテスト関数を透過的に扱えます。

エラーハンドリング

テスト内で例外が投げられると、そのテストは失敗として扱われます。この段階では単純に throw new Error() でアサーションを行っていますが、次のステップで expect() を導入します。