How to test a React app with Node Test Runner
Drastically simplify and speed up testing your React app by switching to Node Test Runner.
Introduction
Testing is a crucial part of software development, ensuring that your application behaves as expected. With the Node.js Test Runner, you can seamlessly integrate testing into your React application. This guide will walk you through the process of setting up and running tests for a React app using the Node.js Test Runner.
I really recommend you to read the Node.js Test Runner guides to understand how node.js test runner works. This guide assumes you have a basic understanding of React and testing.
Installing the Dependencies
First, you need to install the necessary dependencies. In addition to your React app dependencies, you will need the following:
npm install --save-dev @testing-library/react @testing-library/dom jsdom global-jsdom
Note: The rest of the dependencies we will use come from Node.js.
Writing the Component to Be Tested
Let's create a simple React component that we will test. This component will be a counter that increments a value when a button is clicked.
'use client'; import { useState } from 'react'; import styles from './index.module.css'; import type { FC } from 'react'; const Counter: FC = () => { const [count, setCount] = useState(0); return ( <div className={styles.container}> <p className={styles.count}>{count}</p> <button onClick={() => setCount(count + 1)} className={styles.button}> Increment </button> </div> ); }; export default Counter;
index.module.css
.container { @apply flex flex-col items-center justify-center; .count { @apply text-4xl; } .button { @apply px-4 py-2 bg-blue-500 text-white rounded-md; } }
Registering Node.js Loaders
To handle TypeScript and CSS modules, you need to register the appropriate loaders. Create a file named node-hooks/react-test.js
and add the following code:
To understand what is a loader, check out this post.
You'll need to register the loaders for TypeScript and CSS modules:
First let's install the loaders:
npm add -D @nodejs-loaders/tsx @nodejs-loaders/css-module
Then, create the registration file:
import { register } from 'node:module'; import jsdom from 'global-jsdom'; // Register the loaders register('@nodejs-loaders/tsx', import.meta.url); register('@nodejs-loaders/css-module', import.meta.url); jsdom(undefined, { // ⚠️ Failing to specify this will likely lead to many 🤬 url: 'https://test.example.com', });
NOTE: You may need to use
@nodejs-loaders/alias
to allow Node.js to understand path aliases in your TypeScript files.
Writing the Test
Now, let's write a test for the Counter
component. Create a file named index.test.tsx
in the same directory as your component:
import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { render, fireEvent, screen } from '@testing-library/react'; import Counter from './index.ts'; // ⚠️ We need to import the file with the .ts extension describe('Counter', () => { it('should increment the count when the button is clicked', () => { const { unmount } = render(<Counter />); const button = screen.getByRole('button', { name: /increment/i }); const count = screen.getByText('0'); assert.strictEqual(count.textContent, '0'); fireEvent.click(button); assert.strictEqual(count.textContent, '1'); // ⚠️ It's a good idea to unmount the component to prevent it spilling over into the DOM of other tests unmount(); }); });
Structure of a Test File
A typical test file structure includes:
- Imports: Import the necessary modules and components.
- Test Suite: Define a test suite using
describe
. - Test Case: Define individual test cases using
it
. - Render the Component: Render the component to be tested.
- Perform Actions: Simulate user interactions or other actions.
- Assertions: Make assertions to verify the expected behavior.
- Unmount the Component: Clean up by unmounting the component.
Running the Test
To run the test, use the following command:
node --test --import="./node-hooks/typescript.js" --import="./node-hooks/react.js" **/*.test.tsx
You can also add a script to your package.json
to simplify running the tests:
{ "scripts": { "test:unit": "node --test --import=\"./node-hooks/typescript.js\" --import=\"./node-hooks/react.js\" **/*.test.tsx", "test:watch": "node --run test:unit --watch" } }
Note: You can add more patterns to the glob pattern to test more files. For example,
**/*.test.ts
to test all TypeScript files.
And then to call the script you can use --run
:
node --run test:unit # or node --run test:watch
Conclusion
Testing your React app with the Node.js Test Runner is a straightforward process. By following the steps outlined in this guide, you can ensure that your components behave as expected. Happy testing!