Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/miniflare/patterns.md
1# Testing Patterns23## Choosing a Testing Approach45| Approach | Use Case | Speed | Setup | Runtime |6|----------|----------|-------|-------|---------|7| **getPlatformProxy** | Unit tests, logic testing | Fast | Low | Miniflare |8| **Miniflare API** | Integration tests, full control | Medium | Medium | Miniflare |9| **vitest-pool-workers** | Vitest runner integration | Medium | Medium | workerd |1011**Quick guide:**12- Unit tests → getPlatformProxy13- Integration tests → Miniflare API14- Vitest workflows → vitest-pool-workers1516## getPlatformProxy1718Lightweight unit testing - provides bindings without full Worker runtime.1920```js21// vitest.config.js22export default { test: { environment: "node" } };23```2425```js26import { env } from "cloudflare:test";27import { describe, it, expect } from "vitest";2829describe("Business logic", () => {30it("processes data with KV", async () => {31await env.KV.put("test", "value");32expect(await env.KV.get("test")).toBe("value");33});34});35```3637**Pros:** Fast, simple38**Cons:** No full runtime, can't test fetch handler3940## vitest-pool-workers4142Full Workers runtime in Vitest. Reads `wrangler.toml`.4344```bash45npm i -D @cloudflare/vitest-pool-workers46```4748```js49// vitest.config.js50import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";5152export default defineWorkersConfig({53test: {54poolOptions: { workers: { wrangler: { configPath: "./wrangler.toml" } } },55},56});57```5859```js60import { env, SELF } from "cloudflare:test";61import { it, expect } from "vitest";6263it("handles fetch", async () => {64const res = await SELF.fetch("http://example.com/");65expect(res.status).toBe(200);66});67```6869**Pros:** Full runtime, uses wrangler.toml70**Cons:** Requires Wrangler config7172## Miniflare API (node:test)7374```js75import assert from "node:assert";76import test, { after, before } from "node:test";77import { Miniflare } from "miniflare";7879let mf;80before(() => {81mf = new Miniflare({ scriptPath: "src/index.js", kvNamespaces: ["TEST_KV"] });82});8384test("fetch", async () => {85const res = await mf.dispatchFetch("http://localhost/");86assert.strictEqual(await res.text(), "Hello");87});8889after(() => mf.dispose());90```9192## Testing Durable Objects & Events9394```js95// Durable Objects96const ns = await mf.getDurableObjectNamespace("COUNTER");97const stub = ns.get(ns.idFromName("test-counter"));98await stub.fetch("http://localhost/increment");99100// Direct storage101const storage = await mf.getDurableObjectStorage(ns.idFromName("test-counter"));102const count = await storage.get("count");103104// Queue105const worker = await mf.getWorker();106await worker.queue("my-queue", [107{ id: "msg1", timestamp: new Date(), body: { userId: 123 }, attempts: 1 },108]);109110// Scheduled111await worker.scheduled({ cron: "0 0 * * *" });112```113114## Test Isolation & Mocking115116```js117// Per-test isolation118beforeEach(() => { mf = new Miniflare({ kvNamespaces: ["TEST"] }); });119afterEach(() => mf.dispose());120121// Mock external APIs122new Miniflare({123workers: [124{ name: "main", serviceBindings: { API: "mock-api" }, script: `...` },125{ name: "mock-api", script: `export default { async fetch() { return Response.json({mock: true}); } }` },126],127});128```129130## Type Safety131132```ts133import type { KVNamespace } from "@cloudflare/workers-types";134135interface Env {136KV: KVNamespace;137API_KEY: string;138}139140const env = await mf.getBindings<Env>();141await env.KV.put("key", "value"); // Typed!142143export default {144async fetch(req: Request, env: Env) {145return new Response(await env.KV.get("key"));146}147} satisfies ExportedHandler<Env>;148```149150## WebSocket Testing151152```js153const res = await mf.dispatchFetch("http://localhost/ws", {154headers: { Upgrade: "websocket" },155});156assert.strictEqual(res.status, 101);157```158159## Migration from unstable_dev160161```js162// Old (deprecated)163import { unstable_dev } from "wrangler";164const worker = await unstable_dev("src/index.ts");165166// New167import { Miniflare } from "miniflare";168const mf = new Miniflare({ scriptPath: "src/index.ts" });169```170171## CI/CD Tips172173```js174// In-memory storage (faster)175new Miniflare({ kvNamespaces: ["TEST"] }); // No persist = in-memory176177// Use dispatchFetch (no port conflicts)178await mf.dispatchFetch("http://localhost/");179```180181See [gotchas.md](./gotchas.md) for troubleshooting.182