Let's start with the world's simplest test in src/examples/counter/counter.test.tsx:
test('it should render the component', () => {
render(<Counter />);
});And wow, it blows up already! If we look closely, we'll see the following error: ReferenceError: document is not defined.
This makes sense. Vitest runs in one of four environments:
node: The default environment—and the reason whydocumentis not defined.jsdom: Usesjsdomto emulate all of the stuff that you commonly find in the browser, but not in Node.happy-dom: Useshappy-domto emulate most of the browser APIs. It's allegedly faster thanjsdom.edge-runtime: Emulates Vercel's edge runtime.
Some quick notes:
- You need to have these dependencies install in your
package.json. They do not come bundled with Vitest. - I want to use
happy-dombecause it's allegedly faster, but—as of this writing—I've found thatjsdomis still more reliable. Your mileage may vary. Try both out and see what works for you.
We can specify which environment we want by adding a comment to the top of the file.
// @vitest-environment jsdomEverything should pass in our very simple test.
render is a utility that mounts the component and lets you play around with it.
It returns an object with the following properties:
rerender: Triggers a re-render. The props will be passed to yourrenderHookcallback.result: The component that you rendered. This has acurrentproperty, just like arefin React.unmount: Unmounts the component. This could be useful if you want to test any of the cleanup callbacks that are returned fromuseEffect.
Now that we've mounted the component, we can access a particular node that we want to look at.
test('it should render the component', () => {
render(<Counter />);
screen.getByTestId('current-count');
});This test will pass because it doesn't fail. If we misspell current-count, we can watch the test fail because it cannot find that element. In some cases, this might be enough for you. But, let's keep going.
It will also fail if there are more than one matching elements. If you want to find more than one matching element, then you can use getAllByTestId, which will return an array.
Now, if we want to make sure that the count is correct, we can add that expectation to our test.
test('it should render the component', () => {
render(<Counter />);
const currentCount = screen.getByTestId('current-count');
expect(currentCount.innerText).toBe('0');
});Let's make our test fail for a moment:
test('it should render the component', () => {
render(<Counter />);
const currentCount = screen.getByTestId('current-count');
expect(currentCount.textContent).toBe('1');
});Now, we know this won't work because we just validated that the counter starts at 0. We'll see an error message that looks something like this:
AssertionError: expected '0' to be '1' // Object.is equality
❯ src/components/counter/counter.test.tsx:10:34
8| render(<Counter />);
9| const currentCount = screen.getByTestId('current-count');
10| expect(currentCount.textContent).toBe('1');
| ^
11| });
12|
- Expected "1"
+ Received "0"That's not bad, but we can do better. Let's look at extending the built-in matchers for expect with some fancy ones that make it easier to make assertions against DOM nodes.