Skip to content
brought to you byVoidZero

Testing ​

Void apps use Vitest. With Vite+, run your tests with:

sh
vp test

Import it, expect, and other test APIs from vite-plus/test. If you chose Vite instead of Vite+, install vitest as a dev dependency and use vitest for both the imports and command.

Vitest uses your vite.config.ts. Imports such as void/* and @schema work without extra test configuration.

Put tests in tests/ or beside the code they test in src/. Keep them out of convention directories such as routes/ and pages/, where Void would treat them as app routes.

Bindings ​

Tests run in Node.js without starting a Worker or applying local migrations. To test code that uses D1, KV, R2, or another binding, mock the module with vi.mock().

For example, this function counts visits in KV:

ts
// src/visits.ts
import { kv } from 'void/kv';

export async function countVisit(): Promise<number> {
  const visits = ((await kv.get<number>('visits')) ?? 0) + 1;
  await kv.put('visits', visits);
  return visits;
}

Replace KV with an in-memory map in the test:

ts
// src/visits.test.ts
import { expect, it, vi } from 'vite-plus/test';
import { countVisit } from './visits';

vi.mock('void/kv', () => {
  const values = new Map<string, unknown>();
  return {
    kv: {
      get: async (key: string) => values.get(key) ?? null,
      put: async (key: string, value: unknown) => {
        values.set(key, value);
      },
    },
  };
});

it('counts visits', async () => {
  await countVisit();
  expect(await countVisit()).toBe(2);
});

Email ​

void/email/testing captures sent and received email in tests. See Testing in Sending Email and Testing inbound handlers in Receiving Email.