コンテンツにスキップ

テストAPI

Workers Vitest統合は、cloudflare:testモジュールでテストを書くためのランタイムヘルパーを提供します。cloudflare:testモジュールは@cloudflare/vitest-pool-workersパッケージによって提供されますが、Workersランタイムで実行されるテストファイルからのみインポートできます。

cloudflare:testモジュール定義

  • env: import(“cloudflare:test ”).ProvidedEnv

    • ESモジュール形式でエクスポートされたハンドラーに渡される第二引数として使用するためのenvオブジェクトを公開します。これにより、Vitest設定ファイルで定義したバインディングにアクセスできます。


      import { env } from "cloudflare:test";
      it("バインディングを使用する", async () => {
      await env.KV_NAMESPACE.put("key", "value");
      expect(await env.KV_NAMESPACE.get("key")).toBe("value");
      });

      この値の型を設定するには、アンビエントモジュール型を使用します:

      declare module "cloudflare:test" {
      interface ProvidedEnv {
      KV_NAMESPACE: KVNamespace;
      }
      // ...または既存の`Env`型がある場合...
      interface ProvidedEnv extends Env {}
      }
  • SELF: Fetcher

    • main Workerで定義されたデフォルトエクスポートへのサービスバインディングです。これを使用して、Workerに対する統合テストを書くことができます。main Workerはテストと同じアイソレート/コンテキストで実行されるため、すべてのグローバルモックも適用されます。


      import { SELF } from "cloudflare:test";
      it("fetchイベントをディスパッチする", async () => {
      const response = await SELF.fetch("https://example.com");
      expect(await response.text()).toMatchInlineSnapshot(...);
      });
  • fetchMock: import(“undici”).MockAgent

    • アウトバウンドのfetch()リクエストをモックするための宣言型インターフェースです。デフォルトでは無効化されており、各テストファイルを実行する前にリセットされます。詳細については、undiciMockAgentドキュメントを参照してください。これは、現在のテストランナーWorkerのfetch()リクエストのみをモックします。補助Workerは、MiniflareのfetchMock/outboundServiceオプションを使用してfetch()をモックする必要があります。詳細については、設定を参照してください。


      import { fetchMock } from "cloudflare:test";
      import { beforeAll, afterEach, it, expect } from "vitest";
      beforeAll(() => {
      // アウトバウンドリクエストモッキングを有効にする...
      fetchMock.activate();
      // ...アウトバウンドリクエストがモックされていない場合はエラーをスローする
      fetchMock.disableNetConnect();
      });
      // 定義したすべてのモックが一致していることを確認する
      afterEach(() => fetchMock.assertNoPendingInterceptors());
      it("リクエストをモックする", async () => {
      // `https://example.com`への最初のリクエストをモックする
      fetchMock
      .get("https://example.com")
      .intercept({ path: "/" })
      .reply(200, "body");
      const response = await fetch("https://example.com/");
      expect(await response.text()).toBe("body");
      });

イベント

  • createExecutionContext(): ExecutionContext

    • ESモジュール形式でエクスポートされたハンドラーに渡すための第三引数として使用するcontextオブジェクトのインスタンスを作成します。
  • waitOnExecutionContext(ctx:ExecutionContext ): Promise<void>

    • これを使用して、ctx.waitUntil()に渡されたすべてのPromiseが解決されるのを待ってから、サイドエフェクトに対するテストアサーションを実行します。createExecutionContext()によって返されたExecutionContextのインスタンスのみを受け入れます。


      import { env, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
      import { it, expect } from "vitest";
      import worker from "./index.mjs";
      it("fetchハンドラーを呼び出す", async () => {
      const request = new Request("https://example.com");
      const ctx = createExecutionContext();
      const response = await worker.fetch(request, env, ctx);
      await waitOnExecutionContext(ctx);
      expect(await response.text()).toMatchInlineSnapshot(...);
      });
  • createScheduledController(options?:FetcherScheduledOptions ): ScheduledController

    • モジュール形式のscheduled()エクスポートハンドラーに渡すための第一引数として使用するScheduledControllerのインスタンスを作成します。


      import { env, createScheduledController, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
      import { it, expect } from "vitest";
      import worker from "./index.mjs";
      it("スケジュールされたハンドラーを呼び出す", async () => {
      const ctrl = createScheduledController({
      scheduledTime: new Date(1000),
      cron: "30 * * * *"
      });
      const ctx = createExecutionContext();
      await worker.scheduled(ctrl, env, ctx);
      await waitOnExecutionContext(ctx);
      });
  • createMessageBatch(queueName:string , messages:ServiceBindingQueueMessage []): MessageBatch

    • モジュール形式のqueue()エクスポートハンドラーに渡すための第一引数として使用するMessageBatchのインスタンスを作成します。
  • getQueueResult(batch:MessageBatch , ctx:ExecutionContext ): Promise<FetcherQueueResult>

    • MessageBatch内のメッセージの確認済み/再試行状態を取得し、すべてのExecutionContext#waitUntil()されたPromiseが解決されるのを待ちます。createMessageBatch()によって返されたMessageBatchのインスタンスと、createExecutionContext()によって返されたExecutionContextのインスタンスのみを受け入れます。


      import { env, createMessageBatch, createExecutionContext, getQueueResult } from "cloudflare:test";
      import { it, expect } from "vitest";
      import worker from "./index.mjs";
      it("キューハンドラーを呼び出す", async () => {
      const batch = createMessageBatch("my-queue", [
      {
      id: "message-1",
      timestamp: new Date(1000),
      body: "body-1"
      }
      ]);
      const ctx = createExecutionContext();
      await worker.queue(batch, env, ctx);
      const result = await getQueueResult(batch, ctx);
      expect(result.ackAll).toBe(false);
      expect(result.retryBatch).toMatchObject({ retry: false });
      expect(result.explicitAcks).toStrictEqual(["message-1"]);
      expect(result.retryMessages).toStrictEqual([]);
      });

Durable Objects

  • runInDurableObject<O extends DurableObject, R>(stub:DurableObjectStub , callback:(instance: O, state: DurableObjectState) => R | Promise<R>): Promise<R>

    • 提供されたcallbackを、提供されたstubに対応するDurable Objectインスタンス内で実行します。


      これにより、一時的にDurable Objectのfetch()ハンドラーがcallbackに置き換えられ、リクエストが送信され、結果が返されます。これを使用して、Durable Objectインスタンスメソッドを呼び出したり、永続データをシード/取得したりできます。これは、main Workerで定義されたDurable Objectsを指すstubでのみ使用できます。


      export class Counter {
      constructor(readonly state: DurableObjectState) {}
      async fetch(request: Request): Promise<Response> {
      let count = (await this.state.storage.get<number>("count")) ?? 0;
      void this.state.storage.put("count", ++count);
      return new Response(count.toString());
      }
      }
      import { env, runInDurableObject } from "cloudflare:test";
      import { it, expect } from "vitest";
      import { Counter } from "./index.ts";
      it("カウントをインクリメントする", async () => {
      const id = env.COUNTER.newUniqueId();
      const stub = env.COUNTER.get(id);
      let response = await stub.fetch("https://example.com");
      expect(await response.text()).toBe("1");
      response = await runInDurableObject(stub, async (instance: Counter, state) => {
      expect(instance).toBeInstanceOf(Counter);
      expect(await state.storage.get<number>("count")).toBe(1);
      const request = new Request("https://example.com");
      return instance.fetch(request);
      });
      expect(await response.text()).toBe("2");
      });
  • runDurableObjectAlarm(stub:DurableObjectStub ): Promise<boolean>

    • stubが指すDurable Objectのアラームがスケジュールされている場合、即座に実行して削除します。アラームが実行された場合はtrueを、そうでない場合はfalseを返します。これは、main Workerで定義されたDurable Objectsを指すstubでのみ使用できます。
  • listDurableObjectIds(namespace:DurableObjectNamespace ): Promise<DurableObjectId[]>

    • namespace内で作成されたすべてのオブジェクトのIDを取得します。isolatedStorageが有効な場合は、異なるテストで作成されたオブジェクトは返されません。


      import { env, listDurableObjectIds } from "cloudflare:test";
      import { it, expect } from "vitest";
      it("カウントをインクリメントする", async () => {
      const id = env.COUNTER.newUniqueId();
      const stub = env.COUNTER.get(id);
      const response = await stub.fetch("https://example.com");
      expect(await response.text()).toBe("1");
      const ids = await listDurableObjectIds(env.COUNTER);
      expect(ids.length).toBe(1);
      expect(ids[0].equals(id)).toBe(true);
      });

D1

  • applyD1Migrations(db:D1Database , migrations:D1Migration [], migrationTableName?:string ): Promise<void>

    • migrations配列に格納されたすべての未適用のD1マイグレーションをデータベースdbに適用し、migrationsTableNameテーブルにマイグレーションの状態を記録します。migrationsTableNameはデフォルトでd1_migrationsです。Node.js内で@cloudflare/vitest-pool-workers/configパッケージのreadD1Migrations()関数を呼び出してmigrations配列を取得します。マイグレーションを使用したプロジェクトの例については、D1レシピを参照してください。