convex-test-provider
v0.2.0
Published
React provider that adapts convex-test's one-shot client for use with ConvexProvider, so useQuery/useMutation work in tests.
Maintainers
Readme
convex-test-provider
React provider that adapts convex-test's one-shot query/mutation client for use with Convex's ConvexProvider, so useQuery and useMutation work in tests against an in-memory backend.
Install
npm i convex convex-test react
npm i -D convex-test-providerUsage
- Create a convex-test client:
convexTest(schema, modules). - Wrap your component with
ConvexTestProviderand pass the client:
import { convexTest } from "convex-test";
import { ConvexTestProvider } from "convex-test-provider";
import schema from "./convex/schema";
import { modules } from "./convex/test.setup";
const testClient = convexTest(schema, modules);
render(
<ConvexTestProvider client={testClient}>
<YourComponent />
</ConvexTestProvider>
);Query reactivity
This adapter runs each query once (when the component mounts). The UI does not re-render after a mutation in the same test. Assert backend state via client.query(api.your.list, {}), or re-mount to run the query again.
Helper Functions
Reduce test boilerplate from ~15 lines to ~2 lines with createConvexTest.
Setup
Create a test setup file in your Convex directory:
// convex/test.setup.ts
import { createConvexTest, renderWithConvex } from "convex-test-provider";
import schema from "./schema";
export const modules = import.meta.glob("./**/!(*.*.*)*.*s");
export const test = createConvexTest(schema, modules);
export { renderWithConvex };Usage
// src/components/TodoList.test.tsx
import { test, renderWithConvex } from "../../convex/test.setup";
import { expect } from "vitest";
import { api } from "../../convex/_generated/api";
test("creates a todo", async ({ client, seed }) => {
await seed("todos", { text: "Buy milk", completed: false });
const todos = await client.query(api.todos.list, {});
expect(todos).toHaveLength(1);
});Fixtures
| Fixture | Description |
|---------|-------------|
| testClient | Raw convex-test client (unauthenticated) |
| userId | Current user's ID (user auto-created) |
| client | Authenticated client for the current user |
| seed(table, data) | Insert data, auto-fills userId field |
| createUser() | Create another authenticated user |
Multi-user Test Example
test("users only see their own todos", async ({ client, createUser }) => {
// Alice creates a todo
await client.mutation(api.todos.create, { text: "Alice's todo" });
// Bob can't see Alice's todo
const bob = await createUser();
const bobTodos = await bob.query(api.todos.list, {});
expect(bobTodos).toHaveLength(0);
});Configuration
// Custom users table name
export const test = createConvexTest(schema, modules, {
usersTable: "profiles",
});Additional Helpers
wrapWithConvex(children, client)— JSX wrapper for custom renderingrenderWithConvex(ui, client)— Testing Library render with Convex provider
Types
The client prop accepts any object with query(ref, args) and mutation(ref, args) returning promises. The result of convexTest(schema, modules) (and .withIdentity(...)) satisfies this.
Contributing
See CONTRIBUTING.md for the development workflow.
AI agents: See CLAUDE.md for quick reference.
Versioning
Releases follow semantic versioning. See CHANGELOG.md for release history.
