Setting up Jest with NextJS

Random javascript code

I found getting Jest to work with Next required more configuration than I thought. Naive ol’ me assumed I could just install Jest and things would “just work”. After a bunch of trail and error, the end result is relatively simple, but requires several configuration updates. I did not find any official documentation on it, and the only way I could get it to fully work was to use a third party “hack”, but I guess that is modern JS life.

If you tried to add some testing to your Next site and ran into issues, then this post is for you.

TL;DR

I was able to get Jest working inside of Next using Enzyme, a Babel adaptor for Jest, and a third party module that tells Jest to ignore assets that don’t need any transcompilation. Fully working source code is here:

https://github.com/TheRightChoyce/nextjs-jest-starter

Preamble

Jest

Jest is a javascript testing framework. It works out of the box for vanilla javascript, but needs some help to work inside of a React / ES6. I used the babel-jest plugin to help it transpile ES6 code into vanilla js. Note that you do NOT need to explicitly install @babel/core or @babel/preset-env as specified in the Jest docs since Next already includes them.

Enzyme

Enzyme is a utility that helps with testing React components. For any sufficiently meaningful test, you will most likely end up needing to test something that outputs a component (vs just a string or number). This is what Enzyme facilities. 

Jest Transform Stub

This was a small utility I found that helps with CSS and image assets. Without mapping those assets to something (in this case, this module just maps them to an empty string) Jest will force Babel to try and transpile them, which results in several types of errors, but most commonly:

SyntaxError: Unexpected token '.'

  1 | import Head from 'next/head'
  2 | import Image from 'next/image'
> 3 | import styles from '../styles/Home.module.css'
    | ^
  4 |
  5 | export default function Home() {
  6 |   return (

The solution

First:

Install jest and enzyme plus three utilities:

yarn add --dev jest babel-jest enzyme enzyme-adapter-react-16 jest-transform-stub

Here’s what my package.json looks like:

{
	...
  "dependencies": {
    "next": "10.2.0",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "devDependencies": {
    "babel-jest": "^26.6.3",
    "enzyme": "^3.11.0",
    "enzyme-adapter-react-16": "^1.15.6",
    "jest": "^26.6.3",
	"jest-transform-stub": "^2.0.0"
  }
}

Second

Add a .babelrc file to the root of your project with the following:

{
    "presets": ["next/babel"]
}

Sourced from the babel documentation on the Next website: https://nextjs.org/docs/advanced-features/customizing-babel-config

Essentially Jest needs to know about your Babel configuration in order to use it. Thankfully Next already knows about Babel AND defines a preset configuration for you. By adding the Next/Babel preset to the .babelrc file, you are exposing this preset to Jest (and any other plugin).

Third

Create a jest.setup.js file in your project root that contains the following:

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({
    adapter: new Adapter()
});

Sourced from the Enzyme docs here: https://enzymejs.github.io/enzyme/docs/installation/react-16.html

Setting up this adapter in your jest.setup.js file allows any tests created in Jest to know about the Enzyme adapter, and then use Enzyme do its magic to allow you to test JSX and React components. You could do this manually in each test file, but setting it up here allows you to setup once and gain access in all tests.

Fourth

Create a jest.config.js file in your project root that contains the following:

module.exports = {
    
    setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], // Setup jest

    testPathIgnorePatterns: ['/node_modules/', '/.next/'], // Don't test any next tests or tests in the modules
        
    transform: {
        '^.+\\.(js|jsx)$': '<rootDir>/node_modules/babel-jest', // babel .js or .jsx files
        "^.+.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub" // anything style related is ignored and mapped to jest-transform-stub module
    }
}

This config file does two things:

  1. Passes any js or jsx files to Babel for transcompilation
  2. Passes an css, image, or other non-js assets to the jest-transform-stub

The second part was the non-obvious and annoying part to track down. If you look at the source code of the jest-transform-stub module, all it really does is take those non-js assets and return them as empty strings. Without this Jest will barf on images, css files, etc.. WHY is something you should be dealing with? No idea.

For the curious, this stack answer has a bit more detail: https://stackoverflow.com/a/56260594/502572

Finally!

Create a test! Simple tests work fine, but Enzyme’s tooling comes into play once you need to test an actual React component. Here is a quick example:

// Third party imports
import { shallow } from "enzyme";

// Local imports
import Home from '../pages/index';

// Test configuration
describe('Home page', function() {
    test('outputs a component', () => {
        expect(shallow(<Home/>)).not.toBeNull();
    });
});

This simply tests that the Home component exists and can be accessed in Jest. That -should be it! I still need to flush our more tests, but so far so good on my end.

Source code:

Fully working source code for this post is available here:

https://github.com/TheRightChoyce/nextjs-jest-starter

References:

Enzyme / React 16 reference:
https://enzymejs.github.io/enzyme/docs/installation/react-16.html

Jest / Babel reference:
https://jestjs.io/docs/getting-started#using-babel

Next babel reference:
https://nextjs.org/docs/advanced-features/customizing-babel-config

Jest-Transform-Stub:
https://stackoverflow.com/a/56260594/502572
https://github.com/eddyerburgh/jest-transform-stub

Splash image:
Photo by Ferenc Almasi on Unsplash

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s