Designing Tests
CloudAI-X/claude-workflow-v2
Designs and implements testing strategies for any codebase. An agent skill from CloudAI-X/claude-workflow-v2.
Comprehensive testing guide for Cloudflare Workers using Vitest and @cloudflare/vitest-pool-workers.
$ npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install secondsky/claude-skills cloudflare-workers-testing --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/secondsky/claude-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/cloudflare-workers/skills/cloudflare-workers-testing .claude/skills/cloudflare-workers-testing && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "cloudflare-workers-testing" agent skill from https://github.com/secondsky/claude-skills/tree/main/plugins/cloudflare-workers/skills/cloudflare-workers-testing into .claude/skills/cloudflare-workers-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers-testing", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/secondsky/claude-skills/tree/main/plugins/cloudflare-workers/skills/cloudflare-workers-testingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install secondsky/claude-skills cloudflare-workers-testing --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/secondsky/claude-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/cloudflare-workers/skills/cloudflare-workers-testing .agents/skills/cloudflare-workers-testing && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "cloudflare-workers-testing" agent skill from https://github.com/secondsky/claude-skills/tree/main/plugins/cloudflare-workers/skills/cloudflare-workers-testing into .agents/skills/cloudflare-workers-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers-testing", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install secondsky/claude-skills cloudflare-workers-testing --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/secondsky/claude-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/cloudflare-workers/skills/cloudflare-workers-testing .cursor/skills/cloudflare-workers-testing && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "cloudflare-workers-testing" agent skill from https://github.com/secondsky/claude-skills/tree/main/plugins/cloudflare-workers/skills/cloudflare-workers-testing into .cursor/skills/cloudflare-workers-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers-testing", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/secondsky/claude-skills.git --path plugins/cloudflare-workers/skills/cloudflare-workers-testing--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install secondsky/claude-skills cloudflare-workers-testing --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/secondsky/claude-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/cloudflare-workers/skills/cloudflare-workers-testing .gemini/skills/cloudflare-workers-testing && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "cloudflare-workers-testing" agent skill from https://github.com/secondsky/claude-skills/tree/main/plugins/cloudflare-workers/skills/cloudflare-workers-testing into .gemini/skills/cloudflare-workers-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers-testing", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install secondsky/claude-skills cloudflare-workers-testingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/secondsky/claude-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/cloudflare-workers/skills/cloudflare-workers-testing .github/skills/cloudflare-workers-testing && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "cloudflare-workers-testing" agent skill from https://github.com/secondsky/claude-skills/tree/main/plugins/cloudflare-workers/skills/cloudflare-workers-testing into .github/skills/cloudflare-workers-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers-testing", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install secondsky/claude-skills cloudflare-workers-testing --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/secondsky/claude-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/cloudflare-workers/skills/cloudflare-workers-testing .opencode/skills/cloudflare-workers-testing && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "cloudflare-workers-testing" agent skill from https://github.com/secondsky/claude-skills/tree/main/plugins/cloudflare-workers/skills/cloudflare-workers-testing into .opencode/skills/cloudflare-workers-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare-workers-testing", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
cloudflare-workers-testingComprehensive testing guide for Cloudflare Workers using Vitest and @cloudflare/vitest-pool-workers.
Cloudflare Workers Testing is an agent skill from secondsky/claude-skills. Comprehensive testing guide for Cloudflare Workers using Vitest and @cloudflare/vitest-pool-workers. Use for test setup, binding mocks (D1/KV/R2/DO), integration tests, or encountering test failures, mock errors, coverage issues.
Its SKILL.md is about 5.3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 13 other files, including scripts and reference files (for example `references/binding-mocks.md`, `references/coverage-optimization.md` and `references/integration-testing.md`).
It sits in Testing & QA, covering Unit testing, Integration testing and Failing and flaky tests. It works with Cloudflare Workers, Vitest and Cloudflare. The repository describes itself as: Production-ready skills for Claude Code CLI - Cloudflare, React, Tailwind v4, and AI integrations. The licence is MIT.
12 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 8837836. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 2 files in scripts/ (TypeScript and Shell), which the agent can run.
Shell commands in SKILL.md call:
bunbunxFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use bunx, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Cloudflare Workers Testing loads about 5.3k tokens when it runs, and up to ~16k if it reads all its reference files. Until then it costs about 64 tokens; SKILL.md has 824 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from secondsky/claude-skills at commit 8837836, republished under its MIT licence (© secondsky). 824 words, ~5,262 tokens.
.claude/skills/cloudflare-workers-testing/SKILL.md (or your agent's skills folder). This skill also uses 10 other files; get the full folder from GitHub.Status: ✅ Production Ready | Last Verified: 2025-01-27 Vitest: ^2.0.0 | @cloudflare/vitest-pool-workers: 0.7.2 | Miniflare: Latest
Testing Cloudflare Workers with Vitest and @cloudflare/vitest-pool-workers enables writing unit and integration tests that run in a real Workers environment with full binding support (D1, KV, R2, Durable Objects, Queues, AI). Tests execute in Miniflare for local development and can run in CI/CD with actual Workers runtime behavior.
Key capabilities: Binding mocks, execution context testing, edge runtime simulation, coverage tracking, fast test execution.
@cloudflare/vitest-pool-workers 0.7.2 (January 2025):
cloudflare:test module for env/ctx accessMigration from older versions:
# Update dependencies
bun add -D vitest@^2.1.8 @cloudflare/vitest-pool-workers@^0.7.2
# Update vitest.config.ts (new pool configuration format)
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.jsonc' },
miniflare: { compatibilityDate: '2025-01-27' }
}
}
}
});bun add -D vitest @cloudflare/vitest-pool-workersvitest.config.tsimport { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.jsonc' },
miniflare: {
compatibilityDate: '2025-01-27',
compatibilityFlags: ['nodejs_compat']
}
}
}
}
});import { describe, it, expect } from 'vitest';
import { env, createExecutionContext, waitOnExecutionContext } from 'cloudflare:test';
import worker from '../src/index';
describe('Worker', () => {
it('responds with 200', async () => {
const request = new Request('http://example.com/');
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx);
expect(response.status).toBe(200);
});
});bun test
# or
bunx vitestcloudflare:test for Env Access✅ CORRECT:
import { env } from 'cloudflare:test';
it('queries D1', async () => {
const result = await env.DB.prepare('SELECT * FROM users').all();
expect(result.results).toHaveLength(0); // Fresh isolated DB per test
});❌ WRONG:
// Don't manually create env object
const env = { DB: mockDB }; // ❌ Won't use real D1 bindingWhy: cloudflare:test provides real bindings configured from wrangler.jsonc with isolated storage per test.
✅ CORRECT:
it('handles async operations', async () => {
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx); // ✅ Ensures ctx.waitUntil completes
expect(response.status).toBe(200);
});❌ WRONG:
it('missing wait', async () => {
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
// ❌ Missing waitOnExecutionContext - ctx.waitUntil tasks may not complete
expect(response.status).toBe(200);
});Why: Workers use ctx.waitUntil() for background tasks (logging, analytics). Without waiting, these tasks may not complete in tests.
✅ CORRECT:
describe('KV Operations', () => {
it('test 1: writes to KV', async () => {
await env.CACHE.put('key', 'value1');
const val = await env.CACHE.get('key');
expect(val).toBe('value1'); // ✅ Isolated
});
it('test 2: clean state', async () => {
const val = await env.CACHE.get('key');
expect(val).toBeNull(); // ✅ Test 1's data doesn't leak here
});
});Why: Each test runs with fresh binding storage (automatic isolation).
✅ CORRECT:
// vitest.config.ts
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.jsonc' } // ✅ Reads bindings from wrangler
}
}
}
});❌ WRONG:
// vitest.config.ts
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
// ❌ No wrangler config - bindings won't be available
miniflare: { compatibilityDate: '2025-01-27' }
}
}
}
});Why: Wrangler config defines all bindings (D1, KV, R2, etc.). Without it, env will be empty.
✅ CORRECT:
// vitest.config.ts
miniflare: {
compatibilityDate: '2025-01-27' // ✅ Matches wrangler.jsonc
}
// wrangler.jsonc
{
"compatibility_date": "2025-01-27"
}Why: Ensures test environment matches production runtime behavior.
D1 Database:
import { env } from 'cloudflare:test';
it('queries D1', async () => {
// Insert test data
await env.DB.prepare('INSERT INTO users (name) VALUES (?)').bind('Alice').run();
// Query
const result = await env.DB.prepare('SELECT * FROM users WHERE name = ?').bind('Alice').first();
expect(result?.name).toBe('Alice');
});KV Namespace:
it('reads from KV', async () => {
await env.CACHE.put('test-key', 'test-value');
const value = await env.CACHE.get('test-key');
expect(value).toBe('test-value');
});R2 Bucket:
it('uploads to R2', async () => {
await env.BUCKET.put('file.txt', 'Hello World');
const object = await env.BUCKET.get('file.txt');
expect(await object?.text()).toBe('Hello World');
});Durable Objects:
it('interacts with Durable Object', async () => {
const id = env.COUNTER.idFromName('test-counter');
const stub = env.COUNTER.get(id);
const response = await stub.fetch('http://fake/increment');
const data = await response.json();
expect(data.count).toBe(1);
});Unit Test (single function):
import { validateInput } from '../src/utils/validator';
it('validates input', () => {
const result = validateInput({ name: 'Alice', age: 30 });
expect(result.valid).toBe(true);
});Integration Test (full fetch handler):
import worker from '../src/index';
import { env, createExecutionContext, waitOnExecutionContext } from 'cloudflare:test';
it('handles full request flow', async () => {
const request = new Request('http://example.com/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice' })
});
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx);
expect(response.status).toBe(201);
const user = await response.json();
expect(user.name).toBe('Alice');
});Add to vitest.config.ts:
export default defineWorkersConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
include: ['src/**/*.ts'],
exclude: ['src/**/*.test.ts', 'src/**/*.spec.ts'],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80
}
}
}
});Run with coverage:
bunx vitest run --coverageit('creates user via API', async () => {
const request = new Request('http://example.com/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Bob', email: 'bob@example.com' })
});
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx);
expect(response.status).toBe(201);
// Verify DB insert
const user = await env.DB.prepare('SELECT * FROM users WHERE email = ?')
.bind('bob@example.com')
.first();
expect(user?.name).toBe('Bob');
});it('caches API responses', async () => {
// First request (cache miss)
const req1 = new Request('http://example.com/api/data');
const ctx1 = createExecutionContext();
const res1 = await worker.fetch(req1, env, ctx1);
await waitOnExecutionContext(ctx1);
expect(res1.headers.get('X-Cache')).toBe('MISS');
// Second request (cache hit)
const req2 = new Request('http://example.com/api/data');
const ctx2 = createExecutionContext();
const res2 = await worker.fetch(req2, env, ctx2);
await waitOnExecutionContext(ctx2);
expect(res2.headers.get('X-Cache')).toBe('HIT');
});it('handles file upload', async () => {
const formData = new FormData();
formData.append('file', new Blob(['test content'], { type: 'text/plain' }), 'test.txt');
const request = new Request('http://example.com/upload', {
method: 'POST',
body: formData
});
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx);
expect(response.status).toBe(200);
// Verify R2 upload
const object = await env.BUCKET.get('test.txt');
expect(await object?.text()).toBe('test content');
});it('maintains counter state', async () => {
const id = env.COUNTER.idFromName('my-counter');
const stub = env.COUNTER.get(id);
// Increment 3 times
for (let i = 0; i < 3; i++) {
await stub.fetch('http://fake/increment');
}
// Verify state
const response = await stub.fetch('http://fake/value');
const data = await response.json();
expect(data.count).toBe(3);
});it('processes queue messages', async () => {
const messages = [
{ id: '1', body: { action: 'email', to: 'user@example.com' }, timestamp: new Date() }
];
// Simulate queue batch
await worker.queue(
{
queue: 'my-queue',
messages,
retryAll: () => {},
ackAll: () => {}
},
env
);
// Verify processing (check DB, logs, etc.)
const log = await env.DB.prepare('SELECT * FROM email_log WHERE id = ?').bind('1').first();
expect(log?.status).toBe('sent');
});Use descriptive test names:
it('returns 404 when user not found', async () => {});
it('validates email format before saving', async () => {});Test error cases:
it('returns 400 for invalid JSON', async () => {
const request = new Request('http://example.com/api', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: 'invalid json'
});
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx);
expect(response.status).toBe(400);
});Group related tests:
describe('User API', () => {
describe('POST /users', () => {
it('creates user with valid data', async () => {});
it('rejects duplicate email', async () => {});
it('validates required fields', async () => {});
});
});Use beforeEach for setup:
describe('Database tests', () => {
beforeEach(async () => {
// Seed test data
await env.DB.prepare('INSERT INTO users (name) VALUES (?)').bind('Test User').run();
});
it('queries users', async () => {
const result = await env.DB.prepare('SELECT * FROM users').all();
expect(result.results).toHaveLength(1);
});
});Test realistic scenarios:
it('handles concurrent requests', async () => {
const requests = Array(10).fill(null).map(() =>
worker.fetch(new Request('http://example.com/'), env, createExecutionContext())
);
const responses = await Promise.all(requests);
expect(responses.every(r => r.status === 200)).toBe(true);
});Don't share state between tests:
// ❌ BAD: Leaky state
let counter = 0;
it('test 1', () => { counter++; });
it('test 2', () => { expect(counter).toBe(1); }); // Fragile!
// ✅ GOOD: Isolated
it('test 1', () => { const counter = 0; counter++; });
it('test 2', () => { const counter = 0; /* fresh state */ });Don't forget to wait:
// ❌ BAD
const response = await worker.fetch(request, env, ctx);
expect(response.status).toBe(200); // ctx.waitUntil not finished
// ✅ GOOD
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx);
expect(response.status).toBe(200);Don't hardcode URLs:
// ❌ BAD
const request = new Request('http://example.com/test');
// ✅ GOOD
const request = new Request('http://fake-host/test'); // Host doesn't matter in testsDon't test implementation details:
// ❌ BAD: Testing internals
expect(worker.privateHelperFunction).toBeDefined();
// ✅ GOOD: Testing behavior
const response = await worker.fetch(request, env, ctx);
expect(response.status).toBe(200);ReferenceError: env is not definedCause: Not importing env from cloudflare:test.
Fix:
import { env } from 'cloudflare:test'; // ✅ Add this importPrevention: Always use cloudflare:test module for env access.
TypeError: Cannot read property 'DB' of undefinedCause: wrangler.jsonc not loaded in vitest.config.ts.
Fix:
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.jsonc' } // ✅ Add this
}
}
}
});Prevention: Always configure wrangler path in vitest config.
Error: D1_ERROR: no such table: usersCause: D1 database schema not applied in tests.
Fix:
// Option 1: Seed in beforeEach
beforeEach(async () => {
await env.DB.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)
`);
});
// Option 2: Use migrations (load from file)
beforeEach(async () => {
const schema = await fs.readFile('./migrations/schema.sql', 'utf-8');
await env.DB.exec(schema);
});Prevention: Create schema before each test or use shared setup.
Error: ctx.waitUntil tasks did not completeCause: Missing await waitOnExecutionContext(ctx).
Fix:
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
await waitOnExecutionContext(ctx); // ✅ Add thisPrevention: Always wait on execution context in tests.
Error: SELF is not definedCause: Using old SELF.fetch() pattern instead of direct worker import.
Fix:
// ❌ OLD (vitest-pool-workers <0.5)
import { SELF } from 'cloudflare:test';
await SELF.fetch(request);
// ✅ NEW (vitest-pool-workers ≥0.7)
import worker from '../src/index';
await worker.fetch(request, env, ctx);Prevention: Use direct worker imports (modern pattern).
Error: KV.get() returned data from previous testCause: Believing storage is shared (it's not, but may indicate test leak).
Fix: Each test is isolated. If seeing this, check for:
// ❌ Test pollution (shared variable)
let cache = {};
it('test 1', () => { cache.key = 'value'; });
it('test 2', () => { expect(cache.key).toBeUndefined(); }); // Fails!
// ✅ Proper isolation
it('test 1', async () => { await env.CACHE.put('key', 'value1'); });
it('test 2', async () => { const val = await env.CACHE.get('key'); expect(val).toBeNull(); });Prevention: Don't use shared variables for test data.
TypeError: env.BUCKET.put is not a functionCause: R2 binding not configured in wrangler.jsonc.
Fix:
// wrangler.jsonc
{
"r2_buckets": [
{ "binding": "BUCKET", "bucket_name": "test-bucket" }
]
}Prevention: Define all bindings in wrangler config.
Error: Pool 'workers' is not supportedCause: Missing @cloudflare/vitest-pool-workers dependency.
Fix:
bun add -D @cloudflare/vitest-pool-workersPrevention: Install pool package for Workers testing.
Load reference files for detailed, specialized content:
Load references/vitest-setup.md when:
Load references/binding-mocks.md when:
Load references/integration-testing.md when:
Load references/coverage-optimization.md when:
Load references/troubleshooting.md when:
Load templates/vitest-config.ts for:
Load templates/basic-test.ts for:
Load templates/binding-mock-test.ts for:
Load scripts/setup-vitest.sh for:
Load scripts/run-tests.sh for:
For service-specific testing patterns, load:
This skill focuses on cross-cutting Workers testing patterns applicable to ALL binding types and Workers features.
Questions? Load references/troubleshooting.md or use /workers-debug command for interactive help.
© secondsky, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 10 other files (scripts, references) in plugins/cloudflare-workers/skills/cloudflare-workers-testing of secondsky/claude-skills.
Open the folder on GitHubat commit 8837836
Cloudflare Workers Testing next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Cloudflare Workers Testing this skillsecondsky/claude-skills | 227 | — | ~5.3k | Automated safety check: Pass | MIT | |
| Designing TestsCloudAI-X/claude-workflow-v2 | 1.4k | 1 repos | ~1.5k | Automated safety check: Pass | MIT | |
| Writing Unit TestsAsvarox/allkaraoke | 261 | — | ~358 | Automated safety check: Pass | None | |
| Durable Objectshodgef/apiker | 127 | 4 repos | ~1.5k | Automated safety check: Pass | MIT | |
| Test Writing WorkflowiOfficeAI/AionUi | 33k | 1 repos | ~1.2k | Automated safety check: Pass | Apache-2.0 | |
| Ckeditor5 TestingTriliumNext/Trilium | 38k | — | ~3.3k | Automated safety check: Pass | AGPL-3.0 |
CloudAI-X/claude-workflow-v2
Designs and implements testing strategies for any codebase. An agent skill from CloudAI-X/claude-workflow-v2.
Asvarox/allkaraoke
A skill your agent uses when Codex updates, fixes, or adds unit tests in this project.
hodgef/apiker
Create and review Cloudflare Durable Objects. An agent skill from hodgef/apiker.
iOfficeAI/AionUi
Sets the test-writing workflow for the repository: risk-first scenario lists, behavior-focused Vitest tests, a full run before each commit and a coverage target.
TriliumNext/Trilium
Testing CKEditor 5 plugins in the Trilium monorepo. An agent skill from TriliumNext/Trilium.
Ohh-889/skyroc
Generate Vitest + React Testing Library tests for Dify frontend components, hooks, and utilities.
secondsky/claude-skills
TanStack AI (alpha) provider-agnostic type-safe chat with streaming for OpenAI, Anthropic, Gemini, Ollama.
secondsky/claude-skills
AutoAnimate (@formkit/auto-animate) zero-config animations for React.
secondsky/claude-skills
MUI Base UI unstyled React components with Floating UI. An agent skill from secondsky/claude-skills.
secondsky/claude-skills
This skill should be used when the user asks to "upload images to Cloudflare", "implement direct creator upload", "configure image transformations", "optimize WebP/AVIF", "create image variants"…
secondsky/claude-skills
Deploy Next.js to Cloudflare Workers via the OpenNext adapter (@opennextjs/cloudflare).
secondsky/claude-skills
Cloudflare Sandboxes SDK for secure code execution in Linux containers at edge.
Works with
Categories
Comprehensive testing guide for Cloudflare Workers using Vitest and @cloudflare/vitest-pool-workers. Cloudflare Workers Testing is an agent skill from secondsky/claude-skills. Comprehensive testing guide for Cloudflare Workers using Vitest and @cloudflare/vitest-pool-workers.
Cloudflare Workers Testing fits situations like: binding mocks (D1/KV/R2/DO); integration tests; encountering test failures; coverage issues.
Run `npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a claude-code`. Or copy the skill folder (plugins/cloudflare-workers/skills/cloudflare-workers-testing in secondsky/claude-skills) into .claude/skills/cloudflare-workers-testing in your project. Claude Code loads it when a task matches its description.
Run `npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a codex`. Or copy the skill folder (plugins/cloudflare-workers/skills/cloudflare-workers-testing in secondsky/claude-skills) into .agents/skills/cloudflare-workers-testing in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add secondsky/claude-skills --skill cloudflare-workers-testing -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/cloudflare-workers-testing, .gemini/skills/cloudflare-workers-testing, .github/skills/cloudflare-workers-testing and .opencode/skills/cloudflare-workers-testing in your project.
Going by SKILL.md and its folder, Cloudflare Workers Testing needs TypeScript and a shell for the scripts in its folder and the command-line tools its instructions call (bun and bunx). Our summary lists: Node.js; A Bash shell.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Cloudflare Workers Testing is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.3k tokens (SKILL.md is roughly 21k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 11k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Cloudflare Workers Testing: Designing Tests (CloudAI-X/claude-workflow-v2, 1.4k stars), Writing Unit Tests (Asvarox/allkaraoke, 261 stars), Durable Objects (hodgef/apiker, 127 stars) and Test Writing Workflow (iOfficeAI/AionUi, 33k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
secondsky (a GitHub user) maintains it in secondsky/claude-skills, which has 227 GitHub stars. The repository holds 169 skills in this directory. The repository was last updated on September 28, 2026.
Source: secondsky/claude-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.