Testing Next.js App with Jest and RTL
Next.js is like the Swiss army knife of front-end app creation. With the release of Next.js v13, it got even better, introducing server components and other thrilling features.
Tutorials, courses, and docs are floating around everywhere. But one question still lingers, like that last piece of popcorn stuck in your teeth: “How do I test Next.js components or pages, especially considering that they are server components by default?”
But one thing I have never seen is how do I test the Next.js component or page? What makes it different since it is a server component by default.
The only documentation I could find was from the Next.js official site. Let’s do this.
Setup
I’m currently running the most recent version of Next.js, v13.4.2. The first step is to install the required packages.
yarn add -D jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom
It’s a bit baffling that testing isn’t first class in Next.js. It’s an integral part of development, yet it’s not included by default.
Next, create a config file for jest, and put it in the root directory. The name should be jest.config.mjs; you can use jest.config.js if you are not using the import statement.
import nextJest from 'next/jest.js';
const createJestConfig = nextJest({
dir: './',
});
/** @type {import('jest').Config} */
const config = {
testEnvironment: 'jest-environment-jsdom',
};
export default createJestConfig(config);
Just it. Under the hood next/js do the other setup for you.
First Test
To run the test, use the command:
yarn jest
But, since I’m using package json, I aliases it to yarn test
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest",
"test:watch": "jest --watch"
},
My app’s directory structure looks like this:
src
|_ app
|_ page.tsx
|_ layout.tsx
So, I created a test directory to mimic the same structure:
src ...
tests
|_ app
|_ page.spec.tsx
|_ layout.spec.tsx
Since I’m using aliases for importing, we also need to adjust that:
// tsconfig.json
...
"compilerOptions": {
...
"baseUrl": ".",
"paths": {
"@app/*": ["./src/app/*"]
}
...
}
...
// jest.config.mjs
const config = {
...
moduleNameMapper: {
'^@app(.*)$': '<rootDir>/src/app/$1',
}
...
};
Now, let’s dive into our first test:
import { render, screen } from '@testing-library/react';
import Home from '@app/page';
import '@testing-library/jest-dom';
describe('Home', () => {
it('renders a heading', () => {
render(<Home />);
const heading = screen.getByRole('heading', {
name: /Hello world!/i,
});
expect(heading).toBeInTheDocument();
});
});
This test instructs Jest to find the text “Hello world!” in any header tag.
Now let’s run yarn test. It should fail.
Home
✕ renders a heading (176 ms)
● Home › renders a heading
TestingLibraryElementError: Unable to find an accessible element with the role "heading" and name `/Hello world!/i`
Here are the accessible roles:
...
As expected, the test failed. Don’t worry, let’s fix app/page.tsx:
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<h1>Hello world!</h1>
</main>
)
}
Now, let’s run yarn test again:
PASS tests/app/page.spec.tsx
Home
✓ renders a heading (47 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Perfect. We can now tesing the page with jest and react testing library.