cloudflare-workers-miniflare
Wraps Miniflare 3 (the official Cloudflare Workers simulator) and Wrangler dev for testing Workers locally. Covers Miniflare's getMiniflare() programmatic API (workerd-backed simulation matching prod), the wrangler dev local-mode (live-reload during dev), KV / Durable Objects / R2 / D1 bindings emulation, and Vitest + @cloudflare/vitest-pool-workers for in-process tests. Use when testing Cloudflare Workers code locally.
Install with skills.sh (any agent)
npx skills add testland/qa --skill cloudflare-workers-miniflarecloudflare-workers-miniflare
Overview
Miniflare 3 is the official Workers simulator. Per developers.cloudflare.com/workers (opens in new window), Miniflare 3 is built on top of workerd - the same runtime that serves production Workers traffic. This means local-mode behaviour matches prod with very high fidelity.
Three integration patterns:
When to use
Authoring
Install
npm install --save-dev wrangler miniflare @cloudflare/vitest-pool-workers vitestwrangler dev (CLI)
wrangler dev --local # workerd locally; no remote calls
wrangler dev # default = local-mode in Wrangler 3+Now curl http://localhost:8787 hits the local Worker.
Programmatic Miniflare
Per miniflare.dev (opens in new window):
import { Miniflare } from 'miniflare';
const mf = new Miniflare({
scriptPath: './src/worker.js',
modules: true,
kvNamespaces: ['MY_KV'],
d1Databases: ['DB'],
r2Buckets: ['MY_BUCKET'],
durableObjects: {
COUNTER: 'Counter',
},
});
const res = await mf.dispatchFetch('https://example.com/');
expect(res.status).toBe(200);
expect(await res.text()).toBe('Hello');
await mf.dispose();@cloudflare/vitest-pool-workers (recommended for unit tests)
Per developers.cloudflare.com/workers/testing/vitest-integration (opens in new window):
// vitest.config.ts
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.toml' },
},
},
},
});
// src/worker.test.ts
import { env, SELF } from 'cloudflare:test';
import { expect, test } from 'vitest';
test('responds with hello', async () => {
const response = await SELF.fetch('https://example.com/');
expect(await response.text()).toBe('Hello');
});
test('writes to KV', async () => {
await env.MY_KV.put('key', 'value');
expect(await env.MY_KV.get('key')).toBe('value');
});This is the lowest-overhead test path - runs inside workerd.
Durable Objects testing
import { env } from 'cloudflare:test';
test('counter increments', async () => {
const id = env.COUNTER.idFromName('test');
const stub = env.COUNTER.get(id);
const r1 = await stub.fetch('https://example.com/increment');
expect(await r1.text()).toBe('1');
const r2 = await stub.fetch('https://example.com/increment');
expect(await r2.text()).toBe('2');
});Per Cloudflare docs, the vitest-pool-workers env exposes the Durable Object namespace exactly as in production.
D1 testing
import { env } from 'cloudflare:test';
test('d1 query', async () => {
await env.DB.exec('CREATE TABLE IF NOT EXISTS users (id INT, name TEXT)');
await env.DB.prepare('INSERT INTO users VALUES (?, ?)').bind(1, 'alice').run();
const { results } = await env.DB.prepare('SELECT * FROM users').all();
expect(results).toEqual([{ id: 1, name: 'alice' }]);
});Running
npx vitest run # @cloudflare/vitest-pool-workers
wrangler dev # CLI devCI integration
jobs:
workers-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npx vitest runNo CF account/API key needed - vitest-pool-workers runs locally via workerd.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Test against deployed Worker | Slow; flaky; rate-limited | Use vitest-pool-workers |
| Mock the fetch API | Loses Workers' standard-Web-Platform shape | Use real Worker isolation |
| Skip Durable Object tests | Fan-out + consistency bugs hide | Test DO classes directly |
KV getWithMetadata not tested for stale-read behavior | KV is eventually consistent | Test the consistency requirements |
| Local-mode time differs from prod | Cron / scheduled triggers won't fire | Test via deployed Worker + Cloudflare Cron Trigger |
| Hardcoded API key in worker | Exposed in source map / fetch | Use Workers Secrets (wrangler secret) |
| No assertion on response headers | Cache-Control / CORS bugs | Inspect response.headers |
| Cold-start tests against local Worker | Local cold start ~0ms; not representative | Per cold-start-budget-reference, test cold-start budget on deployed Worker |
Limitations
References
Related skills
aws-sam-local-testing
Wraps AWS SAM (Serverless Application Model) Local CLI for testing Lambda functions locally: `sam local invoke` (single invocation with event payload), `sam local start-api` (local API Gateway emulator), `sam local start-lambda` (local Lambda invoke endpoint for AWS SDK clients), and event-payload generation (`sam local generate-event`). For C#/.NET Lambdas, references/dotnet.md covers handler-direct testing with Amazon.Lambda.TestUtilities (TestLambdaContext, TestLambdaLogger) and the dotnet-lambda CLI. Use when testing Lambda + API Gateway + integrated AWS services locally.
azure-functions-tests
Runs Azure Functions locally using Azure Functions Core Tools v4 (`func start`), Azurite storage emulation, and framework-native unit tests for handler code (.NET isolated worker model, Node.js v4, Python v2). Covers HTTP, queue, and timer trigger testing, admin-endpoint invocation for non-HTTP triggers, and binding verification via local.settings.json. Use when testing Azure Functions before deployment, reproducing trigger behaviour without live Azure services, or gating function handler logic in CI.
cold-start-budget-reference
Pure-reference catalog of latency budgets across serverless runtimes: cold starts AND timeouts. Covers AWS Lambda's three-phase cold start (Init: download+unzip+runtime-bootstrap; Init code: imports + module load; Invoke: handler execution), Cloudflare Workers' isolate model (sub-millisecond cold starts via V8 isolates per developers.cloudflare.com), Vercel Edge Runtime, Lambda SnapStart for JVM (snapshot-restore for Java), and provisioned-concurrency trade-offs, plus Lambda timeout + billing budgets in references/timeout-budgets.md (the 15-minute hard limit, getRemainingTimeInMillis, per-ms billing, memory-CPU scaling, the API Gateway 29s / SQS visibility-timeout integration cascade). Use when designing latency or timeout budgets, choosing a runtime, sizing memory, or auditing cold-start variance in production.
serverless-integration-test-builder
Workflow-driven skill that builds the integration-test suite for a serverless application from its IaC definition (SAM template / serverless.yml / Wrangler config / Vercel functions / Netlify functions). Walks through: identifying the function inventory + event sources, picking the right local-emulator per function (sam local / Miniflare / netlify dev / vercel dev / serverless-offline), generating test events per event source, asserting on cold-start + timeout budgets, and emitting the test directory + CI config. Use when introducing integration tests to a serverless project.