コンテンツにスキップ

Miniflare 2のテスト環境からの移行

Miniflare 2は、jest-environment-miniflareおよびvitest-environment-miniflareパッケージでそれぞれJestとVitestのためのカスタム環境を提供していました。@cloudflare/vitest-pool-workersパッケージは、最新のMiniflareバージョンとworkerdランタイムを使用して同様の機能を提供します。workerdはCloudflare Workersを支える同じJavaScript/WebAssemblyランタイムです。workerdを使用することで、テストとデプロイされたコードの間の動作の不一致を実質的に排除できます。詳細については、Miniflare 3の発表を参照してください。

Workers Vitest統合のインストール

まず、古い環境をアンインストールし、新しいプールをインストールする必要があります。Vitest環境はグローバルスコープをカスタマイズすることしかできませんが、プールはまったく異なるランタイムを使用してテストを実行できます。この場合、プールはNode.jsの代わりにworkerd内でテストを実行します。

Terminal window
npm uninstall vitest-environment-miniflare
npm install --save-dev --save-exact vitest@1.5.0
npm install --save-dev @cloudflare/vitest-pool-workers

Vitest設定ファイルの更新

Workers Vitest設定をインストールした後、プールを使用するようにVitest設定ファイルを更新します。以前のMiniflare設定で指定されていたenvironmentOptionsのほとんどは、代わりにpoolOptions.workers.miniflareに移動できます。サポートされているオプションについては、MiniflareのWorkerOptionsインターフェースを参照し、Miniflareバージョン2から3への移行ガイドを参照してください。wrangler.tomlファイルに保存されている設定に依存している場合は、wrangler.configPathも設定してください。

import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig({
test: {
environment: "miniflare",
environmentOptions: { ... },
poolOptions: {
workers: {
miniflare: { ... },
wrangler: { configPath: "./wrangler.toml" },
},
},
},
});

TypeScript設定ファイルの更新

TypeScriptを使用している場合は、正しい環境のtypesを含めるようにtsconfig.jsonを更新します。

{
"compilerOptions": {
...,
"types": [
"@cloudflare/workers-types/experimental"
"vitest-environment-miniflare/globals"
"@cloudflare/vitest-pool-workers"
]
},
}

バインディングへのアクセス

テスト内でバインディングにアクセスするには、cloudflare:testモジュールのenvヘルパーを使用します。

import { it } from "vitest";
import { env } from "cloudflare:test";
it("does something", () => {
const env = getMiniflareBindings();
// ...
});

TypeScriptを使用している場合は、cloudflare:testモジュール内にProvidedEnv interfaceを定義する環境の.d.ts宣言ファイルを追加して、envの型を制御します。

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

隔離ストレージの使用

隔離ストレージはデフォルトで有効になっています。テスト内でsetupMiniflareIsolatedStorage()を含める必要はありません。

const describe = setupMiniflareIsolatedStorage();
import { describe } from "vitest";

waitUntil()との連携

new ExecutionContext()コンストラクタとgetMiniflareWaitUntil()関数は、それぞれcreateExecutionContext()waitOnExecutionContext()に置き換えられました。waitOnExecutionContext()は、すべてのwaitUntil()されたPromiseの結果を解決するPromiseの代わりに、空のPromise<void>を返すことに注意してください。

import { createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
it("does something", () => {
// ...
const ctx = new ExecutionContext();
const ctx = createExecutionContext();
const response = worker.fetch(request, env, ctx);
await getMiniflareWaitUntil(ctx);
await waitOnExecutionContext(ctx);
});

アウトバウンドリクエストのモック

getMiniflareFetchMock()関数は、cloudflare:testモジュールの新しいfetchMockヘルパーに置き換えられました。fetchMockは、getMiniflareFetchMock()の戻り値と同じ型を持っています。fetchMockと以前のgetMiniflareFetchMock()の戻り値の間にはいくつかの違いがあります。

  • fetchMockはデフォルトで無効になっており、以前は有効になっていました。この無効化により、fetchMockを使用していない場合にリクエストボディの不必要なバッファリングが防止されます。fetch()を呼び出す前にfetchMock.activate()を呼び出す必要があります。
  • fetchMockは各テスト実行の開始時にリセットされ、以前の実行で追加されたインターセプターが現在の実行に適用されることはありません。これにより、テスト実行が以前の実行に影響されることがありません。
import { beforeAll, afterAll } from "vitest";
import { fetchMock } from "cloudflare:test";
const fetchMock = getMiniflareFetchMock();
beforeAll(() => {
fetchMock.activate();
fetchMock.disableNetConnect();
fetchMock
.get("https://example.com")
.intercept({ path: "/" })
.reply(200, "data");
});
afterAll(() => fetchMock.assertNoPendingInterceptors());

Durable Objectヘルパーの使用

getMiniflareDurableObjectStorage()getMiniflareDurableObjectState()getMiniflareDurableObjectInstance()、およびrunWithMiniflareDurableObjectGates()関数はすべて、cloudflare:testモジュールの単一のrunInDurableObject()関数に置き換えられました。runInDurableObject()関数は、Durable Objectインスタンスと対応するDurableObjectStateを引数として受け取るコールバックを持つDurableObjectStubを受け取ります。これらの関数を単一の関数に統合することで、APIの表面が簡素化され、インスタンスが正しいリクエストコンテキストとゲーティング動作でアクセスされることが保証されます。詳細については、テストAPIページを参照してください。

import { env, runInDurableObject } from "cloudflare:test";
it("does something", async () => {
const env = getMiniflareBindings();
const id = env.OBJECT.newUniqueId();
const stub = env.OBJECT.get(id);
const storage = await getMiniflareDurableObjectStorage(id);
doSomethingWith(storage);
await runInDurableObject(stub, async (instance, state) => {
doSomethingWith(state.storage);
});
const state = await getMiniflareDurableObjectState(id);
doSomethingWith(state);
await runInDurableObject(stub, async (instance, state) => {
doSomethingWith(state);
});
const instance = await getMiniflareDurableObjectInstance(id);
await runWithMiniflareDurableObjectGates(state, async () => {
doSomethingWith(instance);
});
await runInDurableObject(stub, async (instance) => {
doSomethingWith(instance);
});
});

flushMiniflareDurableObjectAlarms()関数は、cloudflare:testモジュールのrunDurableObjectAlarm()関数に置き換えられました。runDurableObjectAlarm()関数は、単一のDurableObjectStubを受け取り、アラームがスケジュールされ、alarm()ハンドラーが実行された場合はtrueに解決されるPromiseを返します。複数のインスタンスのアラームを「フラッシュ」するには、ループ内でrunDurableObjectAlarm()を呼び出します。

import { env, runDurableObjectAlarm } from "cloudflare:test";
it("does something", async () => {
const env = getMiniflareBindings();
const id = env.OBJECT.newUniqueId();
await flushMiniflareDurableObjectAlarms([id]);
const stub = env.OBJECT.get(id);
const ran = await runDurableObjectAlarm(stub);
});

最後に、getMiniflareDurableObjectIds()関数は、cloudflare:testモジュールのlistDurableObjectIds()関数に置き換えられました。listDurableObjectIds()関数は、厳密な型付けを提供するために、名前空間のstringの代わりにDurableObjectNamespaceインスタンスを受け取ります。listDurableObjectIds()関数は、隔離ストレージを尊重します。有効になっている場合、他のテストで作成されたオブジェクトのIDは返されません。

import { env, listDurableObjectIds } from "cloudflare:test";
it("does something", async () => {
const ids = await getMiniflareDurableObjectIds("OBJECT");
const ids = await listDurableObjectIds(env.OBJECT);
});