Cloudflare Workers are fast, lightweight, and TypeScript-friendly — but testing them often feels confusing at first.
This guide walks through adding Vitest to an existing Cloudflare Worker project, explains why each step exists, and clears up common confusions (like jsdom, coverage packages, and where tests should live).
By the end, you’ll have:
- Vitest running cleanly
- A sensible folder structure
- Zero unnecessary dependencies
- A setup that fits Cloudflare Workers (not browsers)
Starting Point: Existing Worker Project
Assume your project looks like this:
.
├── package.json
├── package-lock.json
├── public/
│ └── index.html
├── src/
│ └── index.ts
├── tsconfig.json
├── worker-configuration.d.ts
└── wrangler.jsonc
This is a TypeScript Cloudflare Worker, likely using Wrangler.
Why Vitest?
Vitest is a modern test runner built on Vite. It’s a great fit because:
- TypeScript works out of the box
- Very fast startup and watch mode
- Jest-style API (
describe,it,expect) - Minimal configuration
- Works cleanly with Worker / Node-style environments
For new TypeScript projects, Vitest is usually the best choice.
Step 1: Install Vitest (and Only What You Need)
Run this once:
npm install -D vitest @vitest/coverage-v8
What this actually installs
vitest
The test runner itself. This is required.@vitest/coverage-v8
Optional but useful. It enables code-coverage reporting using V8’s native engine.
Both are installed as dev dependencies (-D) because:
- tests run locally or in CI
- they are never deployed to Cloudflare
❌ What you do not need
You might see advice to install jsdom.
Do not install it for Cloudflare Workers.
Workers:
- do not have
documentorwindow - are not browser environments
So skip:
npm install -D jsdom ❌
Step 2: Add a Vitest Config
Create vitest.config.ts at the project root:
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'node'
}
})
Why environment: 'node'?
Cloudflare Workers behave much closer to Node-style runtimes than browsers.
- No DOM
- No
window - No
document
Using node avoids fake browser APIs and keeps tests honest.
Step 3: Add Test Scripts
Update package.json:
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage"
}
}
When to use which
-
npm test→ watch mode (great during development) -
npm run test:run→ one-off run (CI-friendly) -
npm run test:coverage→ generates coverage stats
Step 4: Where Should Tests Live?
Put tests inside src/, next to the code they test.
Recommended structure
src/
├── index.ts
├── index.test.ts
As the project grows:
src/
├── handlers/
│ ├── auth.ts
│ └── auth.test.ts
├── utils/
│ ├── math.ts
│ └── math.test.ts
Why this works best
- Tests stay close to the code
- Imports are simple (
./index) - Easy to refactor
- Vitest finds these files automatically
Vitest already looks for:
**/*.test.ts
**/*.spec.ts
No extra configuration needed.
Step 5: Example Test
src/index.ts
export function add(a: number, b: number) {
return a + b
}
src/index.test.ts
import { describe, it, expect } from 'vitest'
import { add } from './index'
describe('add', () => {
it('adds two numbers', () => {
expect(add(2, 3)).toBe(5)
})
})
Run it:
npm test
Or once:
npx vitest run
Step 6: Make Sure Tests Aren’t Deployed
Ensure your tsconfig.json excludes test files:
{
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
}
This guarantees:
- test files are never bundled
- Workers stay lean
Summary
- ✅ Use Vitest for Cloudflare Workers
- ✅ Install
vitest(coverage optional) - ❌ Do not install
jsdom - 📁 Put tests inside
src/ - ⚙️ Use
environment: 'node' - 🚀 You’re production-ready and CI-friendly
This setup is minimal, modern, and scales cleanly as your Worker grows.
Top comments (0)