Vitest
RedwoodSDK supports integration testing using Vitest and Cloudflare Workers Pool.
The “Test Bridge” Pattern
Section titled “The “Test Bridge” Pattern”Since tests run in an isolated worker process (powered by vitest-pool-workers), they cannot directly access your running application’s state or database bindings in the same way a unit test might.
To bridge this gap, this guide uses a pattern where the test runner communicates with your worker via a special HTTP route (/_test).
- Test Side: Uses an
vitestInvokehelper to send a POST request with the action name and arguments. - Worker Side: A
handleVitestRequesthandler receives the request, executes the actual Server Action within the worker’s context (with full access toctx, D1, KV, etc.), and returns the result.
You can interpret this as “RPC from Test Runner to Worker”.
1. Configure Vitest
Section titled “1. Configure Vitest”You will need two things in your vitest.config.ts:
- Use
defineWorkersConfigfrom@cloudflare/vitest-pool-workers/config. - Point the pool to your built
wrangler.json.
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig({ test: { include: ["src/**/*.test.{ts,tsx}"], poolOptions: { workers: { wrangler: { // Use the built worker output so `rwsdk/worker` and RSCs resolve correctly. configPath: "./dist/worker/wrangler.json", }, }, }, },});2. Setup the Test Bridge
Section titled “2. Setup the Test Bridge”Expose a /_test route in your src/worker.tsx to handle incoming test requests using rwsdk-community.
You can find a complete working example in our Vitest Playground.
import { render, route } from "rwsdk/router";import { defineApp } from "rwsdk/worker";import { handleVitestRequest } from "rwsdk-community/worker";import * as appActions from "./app/actions";import * as testUtils from "./app/test-utils";
export default defineApp([ // ... other middleware
// 1. Expose the test bridge route route("/_test", { post: ({ request }) => handleVitestRequest(request, { ...appActions, ...testUtils // Optional: expose specific test utilities }), }),
// ... your application routeswe use render(Document, [route("/", Home)]),]);3. Write a Test
Section titled “3. Write a Test”Use the vitestInvoke helper from rwsdk-community/test to call your exposed actions.
import { expect, it, describe, beforeAll } from "vitest";import { vitestInvoke } from "rwsdk-community/test";
describe("Integration Test", () => { it("should create an item", async () => { // 1. Call a server action via the bridge const id = await vitestInvoke<number>("createItem", "Test Item");
// 2. Verify result expect(id).toBeGreaterThan(0);
// 3. Verify side effects (e.g. ask DB for count) const count = await vitestInvoke<number>("getItemCount"); expect(count).toBe(1); });});