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 whydocument
is not defined.jsdom
: Usesjsdom
to emulate all of the stuff that you commonly find in the browser, but not in Node.happy-dom
: Useshappy-dom
to 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-dom
because it's allegedly faster, but—as of this writing—I've found thatjsdom
is 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 jsdom
Everything 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 yourrenderHook
callback.result
: The component that you rendered. This has acurrent
property, just like aref
in 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.