Testland
Browse all skills & agents

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-miniflare
View source

cloudflare-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:

  1. wrangler dev (CLI; live-reload during dev).
  2. Programmatic Miniflare (getMiniflare(); for integration tests).
  3. @cloudflare/vitest-pool-workers (Vitest + in-process Workers; the recommended unit-test path).

When to use

  • Unit / integration tests for Cloudflare Workers code.
  • Tests for KV / Durable Objects / R2 / D1 bindings.
  • Local-development of Workers without deploying.

Authoring

Install

npm install --save-dev wrangler miniflare @cloudflare/vitest-pool-workers vitest

wrangler 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 dev

CI 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 run

No CF account/API key needed - vitest-pool-workers runs locally via workerd.

Anti-patterns

Anti-patternWhy it failsFix
Test against deployed WorkerSlow; flaky; rate-limitedUse vitest-pool-workers
Mock the fetch APILoses Workers' standard-Web-Platform shapeUse real Worker isolation
Skip Durable Object testsFan-out + consistency bugs hideTest DO classes directly
KV getWithMetadata not tested for stale-read behaviorKV is eventually consistentTest the consistency requirements
Local-mode time differs from prodCron / scheduled triggers won't fireTest via deployed Worker + Cloudflare Cron Trigger
Hardcoded API key in workerExposed in source map / fetchUse Workers Secrets (wrangler secret)
No assertion on response headersCache-Control / CORS bugsInspect response.headers
Cold-start tests against local WorkerLocal cold start ~0ms; not representativePer cold-start-budget-reference, test cold-start budget on deployed Worker

Limitations

  • Workerd local-mode mirrors prod with ~95% fidelity. Edge- cases around network egress, KV durability, R2 ranges may differ. Test against staging for high-fidelity.
  • No production analytics in local-mode. Workers Analytics Engine writes are no-ops locally.
  • Bindings to remote services (e.g., remote KV from a different account) need explicit remote: true config in Wrangler 3.
  • Cron Triggers don't fire locally. Schedule events need manual dispatchFetch('https://example.com/__scheduled').
  • WebSocket Hibernation (Durable Objects) has subtleties in local mode; verify against deployed.

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`). 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 cold-start budgets across serverless runtimes. 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. Includes per-runtime typical cold-start ranges and the testable behaviours each model creates. Use when designing latency budgets, choosing a runtime, or auditing cold-start variance in production.

lambda-test-tools-net

Wraps Amazon.Lambda.TestTool (the canonical .NET Lambda local-testing toolkit from github.com/aws/aws-lambda-dotnet) for invoking Lambda handlers from xUnit / NUnit tests with simulated AWS Lambda contexts (ILambdaContext, ILambdaSerializer). Covers handler-direct invocation, mock context fixtures, the dotnet-lambda CLI, and integration with the .NET LambdaSerializer for JSON. Use when testing AWS Lambda functions written in C#/.NET.

lambda-timeout-budget-reference

Pure-reference catalog of AWS Lambda timeout + billing semantics. Covers Lambda's hard 15-minute (900s) wall-clock limit, the timeout-vs-deadline relationship (Lambda Context.getRemainingTimeInMillis), per-invocation billing (rounded to 1ms; per-invocation + duration × memory), the memory-vs-CPU relationship (CPU scales linearly with memory), the integration-timeout cascade (API Gateway 29s → Lambda 15min; SQS visibility-timeout vs Lambda timeout), and per-runtime nuances. Use when designing a Lambda's timeout config, debugging timeout-vs-billing surprises, or sizing memory for compute-bound workloads.

netlify-functions-tests

Wraps Netlify Functions testing patterns: Netlify Dev (`netlify dev`) for local routing emulation, the @netlify/functions handler API testing pattern, Netlify Edge Functions (Deno runtime) vs Background Functions (Lambda under the hood) distinction, and scheduled-function (cron) test patterns. Use when testing Netlify Functions or Edge Functions.

serverless-framework-test-plugin

Wraps the Serverless Framework (serverless.com) test ecosystem: serverless-offline (local HTTP emulator), serverless-jest-plugin / serverless-mocha-plugin (per-runtime test runners), and the `serverless invoke local` CLI for one-off invocations. Use when testing Lambda functions deployed via the Serverless Framework.

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.

vercel-edge-runtime-testing

Wraps Vercel Edge Runtime testing patterns: the @edge-runtime/jest-environment + edge-runtime CLI for executing Web-Standard APIs (Request / Response / fetch) in jest tests, the `vercel dev` local emulator for full route testing, and the Edge vs Node Function divergence (no fs, no Buffer; Request / Response only). Covers the 30s Edge function timeout per vercel.com/docs. Use when testing Vercel Edge Functions or middleware.