Skip to main content
Version: Next

Testing

Wasp provides helpers for testing your client code with Vitest.

Testing server code​

Wasp doesn't provide helpers for testing server code yet. You can follow the progress in this GitHub issue.

Setting up client tests​

Installing the packages​

Install Vitest and the libraries for testing React components:

npm install -D vitest jsdom @testing-library/react @testing-library/jest-dom msw

Configuring Vitest​

Vitest reads its configuration from your vite.config.ts, so your tests use the same Vite setup as your app. Add a test section to it, built with Wasp's waspTestConfig helper:

vite.config.js
import { defineConfig } from "vitest/config";
import { wasp } from "wasp/client/vite";
import { waspTestConfig } from "wasp/test";

export default defineConfig({
plugins: [wasp()],
test: waspTestConfig({
setupFiles: ["./src/test/setup.ts"],
}),
});

waspTestConfig sets the Vitest options a Wasp app needs, so Vitest:

  • runs the test files in your src folder (e.g., TaskList.test.tsx or helpers.spec.js),
  • runs them in jsdom, a simulated browser, so your components can render,
  • gives your client code a placeholder server URL, since your tests mock the server instead of running it.

The options you pass to waspTestConfig are regular Vitest options, and they override Wasp's. Here, setupFiles runs src/test/setup.ts before each test file. Create it:

src/test/setup.js
import "@testing-library/jest-dom/vitest";
import { cleanup } from "@testing-library/react";
import { afterEach } from "vitest";

afterEach(() => {
cleanup();
});

It prepares every test file in two ways:

  • It adds matchers for checking the page, like expect(button).toBeInTheDocument().
  • It removes what a test rendered when the test ends, so the next test starts with an empty page.

Running your tests​

Add a script for running your tests to your package.json:

package.json
{
"scripts": {
"test": "vitest"
}
}

Your tests import code from the Wasp SDK, which Wasp generates when it compiles your app. Keep wasp start running in one terminal, and run your tests in another:

npm test

Vitest watches your files and reruns the affected tests when you change them. In CI, it runs the tests once instead. There, generate the SDK with wasp compile before running the tests:

wasp compile
npm test

Running tests visually​

Vitest has a UI that shows your test results in the browser. Install it and start Vitest with it:

npm install -D @vitest/ui
npm test -- --ui

Writing client tests​

Testing plain functions​

Functions that don't render components or call your server (through queries, actions, or APIs) need no extra setup. Import them and test them like in any other project:

src/tasks/helpers.js
export function areThereAnyTasks(tasks) {
return tasks.length !== 0;
}
src/tasks/helpers.test.js
import { expect, test } from "vitest";
import { areThereAnyTasks } from "./helpers";

test("areThereAnyTasks", () => {
expect(areThereAnyTasks([])).toBe(false);
});

Testing components​

Wasp wraps every page of your app in providers: React Query for queries and actions, and React Router for links. A component that uses queries, actions, or links fails to render without them.

renderInContext from wasp/client/test renders a component inside the same providers:

src/tasks/TasksHeader.jsx
import { Link } from "wasp/client/router";

export function TasksHeader() {
return (
<div>
<h1>Your tasks</h1>
<Link to="/">Back home</Link>
</div>
);
}
src/tasks/TasksHeader.test.jsx
import { screen } from "@testing-library/react";
import { expect, test } from "vitest";
import { renderInContext } from "wasp/client/test";
import { TasksHeader } from "./TasksHeader";

test("links back home", () => {
renderInContext(<TasksHeader />);

expect(screen.getByRole("link", { name: "Back home" })).toBeInTheDocument();
});

Mocking your queries​

A component that calls useQuery sends a request to your server. To test the component without running the server, mock the query's response.

Call mockServer once at the top of your test file. It starts a mock server for the file and returns mockQuery, which takes a query and the data it should return:

src/tasks/TaskList.jsx
import { getTasks, useQuery } from "wasp/client/operations";

export function TaskList() {
const { data: tasks } = useQuery(getTasks);

return (
<ul>
{tasks?.map((task) => (
<li key={task.id}>
<input type="checkbox" checked={task.isDone} readOnly />
{task.description}
</li>
))}
</ul>
);
}
src/tasks/TaskList.test.jsx
import { screen } from "@testing-library/react";
import { expect, test } from "vitest";
import { getTasks } from "wasp/client/operations";
import { mockServer, renderInContext } from "wasp/client/test";
import { TaskList } from "./TaskList";

const { mockQuery } = mockServer();

test("shows the tasks", async () => {
mockQuery(getTasks, [
{ id: 1, description: "Buy milk", isDone: true, userId: 1 },
]);

renderInContext(<TaskList />);

await screen.findByText("Buy milk");
expect(screen.getByRole("checkbox")).toBeChecked();
});

mockServer resets the mocks after each test, so every test mocks the data it needs.

Mocking your APIs​

If your component calls one of your APIs, mock the API with mockApi. It takes the API's HTTP method and path, and the JSON it should respond with:

src/tasks/TaskStats.jsx
import { useEffect, useState } from "react";
import { api } from "wasp/client/api";

export function TaskStats() {
const [doneTasks, setDoneTasks] = useState();

useEffect(() => {
api
.get("/stats")
.json()
.then((stats) => setDoneTasks(stats.doneTasks));
}, []);

return <p>{doneTasks} tasks done</p>;
}
src/tasks/TaskStats.test.jsx
import { screen } from "@testing-library/react";
import { test } from "vitest";
import { HttpMethod } from "wasp/client";
import { mockServer, renderInContext } from "wasp/client/test";
import { TaskStats } from "./TaskStats";

const { mockApi } = mockServer();

test("shows the stats", async () => {
mockApi({ method: HttpMethod.Get, path: "/stats" }, { doneTasks: 3 });

renderInContext(<TaskStats />);

await screen.findByText("3 tasks done");
});

API reference​

API reference

waspTestConfig »

Configure Vitest with the defaults a Wasp app needs.

API reference

renderInContext »

Render a component with the providers Wasp needs.

API reference

mockServer »

Set up a mock server and mock your Queries and APIs.

API reference

WaspTestWrapper »

Wrap your own Testing Library renders in the providers Wasp needs.