Testing
Wasp is in beta, so keep in mind there might be some kinks / bugs, and possibly some changes with testing support in the future. If you encounter any issues, reach out to us on Discord and we will make sure to help you out!
Testing Your React Appβ
Wasp enables you to quickly and easily write both unit tests and React component tests for your frontend code. Because Wasp uses Vite, we support testing web apps through Vitest.
Included Libraries
vitest: Unit test framework with native Vite support.
@vitest/ui: A nice UI for seeing your test results.
jsdom: A web browser test environment for Node.js.
@testing-library/react / @testing-library/jest-dom: Testing helpers.
msw: A server mocking library.
Writing Testsβ
For Wasp to pick up your tests, they should be placed within the src directory and use an extension that matches these glob patterns. Some of the file names that Wasp will pick up as tests:
yourFile.test.tsYourComponent.spec.jsx
Within test files, you can import your other source files as usual. For example, if you have a component Counter.jsx, you test it by creating a file in the same directory called Counter.test.jsx and import the component with import Counter from './Counter'.
Running Testsβ
Running wasp test client will start Vitest in watch mode and recompile your Wasp project when changes are made.
- If you want to see a realtime UI, pass
--uias an option. - To run the tests just once, use
wasp test client run.
All arguments after wasp test client are passed directly to the Vitest CLI, so check out their documentation for all of the options.
You should not run wasp test while wasp start is running. Both will try to compile your project to .wasp/out.
React Testing Helpersβ
Wasp provides several functions to help you write React tests:
-
renderInContext: Takes a React component, wraps it inside aQueryClientProviderandRouter, and renders it. This is the function you should use to render components in your React component tests.import{ renderInContext }from"wasp/client/test";
renderInContext(<MainPage/>); -
mockServer: Sets up the mock server and returns an object containing themockQueryandmockApiutilities. This should be called outside of any test case, in each file that wants to use those helpers.import{ mockServer }from"wasp/client/test";
const{ mockQuery, mockApi }=mockServer();-
mockQuery: Takes a Wasp query to mock and the JSON data it should return.import{ getTasks }from"wasp/client/operations";
mockQuery(getTasks,[]);- Helpful when your component uses
useQuery. - Behind the scenes, Wasp uses
mswto create a server request handle that responds with the specified data. - Mock are cleared between each test.
- Helpful when your component uses
-
mockApi: Similar tomockQuery, but for APIs. Instead of a Wasp query, it takes a route containing an HTTP method and a path.import{HttpMethod}from"wasp/client";
mockApi({method:HttpMethod.Get,path:"/foor/bar"},{res:"hello"});
-
Testing Your Server-Side Codeβ
Wasp currently does not provide a way to test your server-side code, but we will be adding support soon. You can track the progress at this GitHub issue and express your interest by commenting.
Examplesβ
You can see some tests in a Wasp project here.
Client Unit Testsβ
- JavaScript
- TypeScript
exportfunctionareThereAnyTasks(tasks){
return tasks.length===0;
}
import{ test, expect }from"vitest";
import{ areThereAnyTasks }from"./helpers";
test("areThereAnyTasks",()=>{
expect(areThereAnyTasks([])).toBe(false);
});
import{typeTask}from"wasp/entities";
exportfunctionareThereAnyTasks(tasks: Task[]):boolean{
return tasks.length ===0;
}
import{ test, expect }from"vitest";
import{ areThereAnyTasks }from"./helpers";
test("areThereAnyTasks",()=>{
expect(areThereAnyTasks([])).toBe(false);
});
React Component Testsβ
- JavaScript
- TypeScript
import{ useQuery, getTasks }from"wasp/client/operations";
constTodo=(_props)=>{
const{data: tasks }=useQuery(getTasks);
return(
<ul>
{tasks &&
tasks.map((task)=>(
<likey={task.id}>
<inputtype="checkbox"value={task.isDone}/>
{task.description}
</li>
))}
</ul>
);
};
import{ test, expect }from"vitest";
import{ screen }from"@testing-library/react";
import{ mockServer, renderInContext }from"wasp/client/test";
import{ getTasks }from"wasp/client/operations";
importTodofrom"./Todo";
const{ mockQuery }=mockServer();
const mockTasks =[
{
id:1,
description:"test todo 1",
isDone:true,
userId:1,
},
];
test("handles mock data",async()=>{
mockQuery(getTasks, mockTasks);
renderInContext(<Todo/>);
await screen.findByText("test todo 1");
expect(screen.getByRole("checkbox")).toBeChecked();
screen.debug();
});
import{ useQuery, getTasks }from"wasp/client/operations";
constTodo=(_props:{})=>{
const{ data: tasks }=useQuery(getTasks);
return(
<ul>
{tasks &&
tasks.map((task)=>(
<likey={task.id}>
<inputtype="checkbox"value={task.isDone}/>
{task.description}
</li>
))}
</ul>
);
};
import{ test, expect }from"vitest";
import{ screen }from"@testing-library/react";
import{ mockServer, renderInContext }from"wasp/client/test";
import{ getTasks }from"wasp/client/operations";
importTodofrom"./Todo";
const{ mockQuery }=mockServer();
const mockTasks =[
{
id:1,
description:"test todo 1",
isDone:true,
userId:1,
},
];
test("handles mock data",async()=>{
mockQuery(getTasks, mockTasks);
renderInContext(<Todo/>);
await screen.findByText("test todo 1");
expect(screen.getByRole("checkbox")).toBeChecked();
screen.debug();
});
Testing With Mocked APIsβ
- JavaScript
- TypeScript
import{ api }from"wasp/client/api";
constTodo=(_props)=>{
const[tasks, setTasks]=useState([]);
useEffect(()=>{
api
.get("/tasks")
.then((res)=> res.json())
.then((tasks)=>setTasks(tasks))
.catch((err)=>window.alert(err));
});
return(
<ul>
{tasks &&
tasks.map((task)=>(
<likey={task.id}>
<inputtype="checkbox"value={task.isDone}/>
{task.description}
</li>
))}
</ul>
);
};
import{ test, expect }from"vitest";
import{ screen }from"@testing-library/react";
import{ mockServer, renderInContext }from"wasp/client/test";
importTodofrom"./Todo";
const{ mockApi }=mockServer();
const mockTasks =[
{
id:1,
description:"test todo 1",
isDone:true,
userId:1,
},
];
test("handles mock data",async()=>{
mockApi("/tasks",{res: mockTasks });
renderInContext(<Todo/>);
await screen.findByText("test todo 1");
expect(screen.getByRole("checkbox")).toBeChecked();
screen.debug();
});
import{typeTask}from"wasp/entities";
import{ api }from"wasp/client/api";
constTodo=(_props:{})=>{
const[tasks, setTasks]=useState<Task>([]);
useEffect(()=>{
api
.get("/tasks")
.then((res)=> res.json()asTask[])
.then((tasks)=>setTasks(tasks))
.catch((err)=>window.alert(err));
});
return(
<ul>
{tasks &&
tasks.map((task)=>(
<likey={task.id}>
<inputtype="checkbox"value={task.isDone}/>
{task.description}
</li>
))}
</ul>
);
};
import{ test, expect }from"vitest";
import{ screen }from"@testing-library/react";
import{ mockServer, renderInContext }from"wasp/client/test";
importTodofrom"./Todo";
const{ mockApi }=mockServer();
const mockTasks =[
{
id:1,
description:"test todo 1",
isDone:true,
userId:1,
},
];
test("handles mock data",async()=>{
mockApi("/tasks", mockTasks);
renderInContext(<Todo/>);
await screen.findByText("test todo 1");
expect(screen.getByRole("checkbox")).toBeChecked();
screen.debug();
});