Next.js is advertised as the React framework for production. It enables React.js developers to develop production-grade applications by providing out-of-the-box features like routing, code-splitting, bundling, Typescript, and built-in CSS support. These are required features for production application development. In this tutorial, I’ll show you how to continuously integrate features into your Next.js applications by setting up a testing framework to automatically test features added to our application and ensure it doesn’t break in the process.

Prerequisites

To follow this tutorial, a few things are required:

  1. Basic knowledge of Javascript
  2. Node.js (version >= 10.13) installed on your system
  3. A CircleCI account
  4. A GitHub account

With all of these installed and set up, let’s begin the tutorial.

Creating a new Next.js project

To begin, create a new Next.js project by running the following command:

npx create-next-app next-testing

Note: If you’re using Node.js version 13 you will need your version to be >=13.7 for this command to run successfully.

This will automatically create a Next.js application inside a next-testing folder (you can give the folder any name you chose). Once the scaffolding process is done, go into the root of the project and run the application:

cd next-testing
npm run dev

This will boot up a development server that serves the application at http://localhost:3000. Load this URL in your browser.

New App (Local) - Next.js

Installing and setting up Jest for testing

Your next step is to set up the testing framework and the utilities needed to run tests. We will be using Jest as our testing framework and installing a bunch of utilities to ensure that our tests run smoothly. Below is a list of the packages we will be installing:

  • jest: The testing framework
  • @testing-library/jest-dom: Extends jest by providing custom matchers to test the state of the DOM
  • @testing-library/react: The React testing library provides simple and complete React DOM testing utilities
  • @testing-library/dom: The base testing library for testing DOM nodes
  • babel-jest: Used for transpiling Javascript in our test suites

Install these packages at once with the following command:

npm install -D jest @testing-library/react @testing-library/jest-dom @testing-library/dom babel-jest

Once these have been installed, the next step is to create a .babelrc configuration file to instruct babel-jest to use the custom preset for Next.js. Create the .babelrc file at the root of your project and enter the following configuration:

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

Next, we need to configure jest to do the following:

  • Ignore the .next build folder and the node_modules folder
  • Use babel-jest to transpile Javascript in our tests
  • Mock static files (CSS imports and files) in our tests

Inside the package.json file, add the following section for jest:

...
"jest": {
    "testPathIgnorePatterns": [
      "<rootDir>/.next/",
      "<rootDir>/node_modules/"
    ],
    "transform": {
      "^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest"
    },
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
    }
}
...

While still in the package.json file, add the test script to the scripts section:

...
"scripts": {
    ...
    "test": "jest"
},
...

Mocking static assets for Jest tests

In the previous section, we added a jest configuration to instruct jest to mock CSS file imports and other static files in our tests. This is done because these types of files are not useful in tests so we can safely mock them out. While writing the settings for this, you will notice that we pointed to two mock files fileMock.js and styleMock.js which are used to mock static files and CSS files respectively.

Let’s create these files. Create a __mocks__ folder at the root of the project. Then create the fileMock.js file and enter the following code:

module.exports = "placeholder-file";

Create the styleMock.js file inside this __mocks__ folder and enter the following code:

module.exports = {};

With these files, you are able to safely mock static assets and CSS imports.

Rendering React.js components

Now we can begin writing tests. The project contains everything it needs to run them. Create a __tests__ folder (this is a special folder where jest looks for test files) at the root of the project and add a test file named HomeTest.js. Enter the following code in the file:

import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import Home from "../pages/index";

test("Check for Getting Started Text", () => {
  const { getByText } = render(<Home />);
  expect(getByText("Get started by editing")).toBeInTheDocument();
});

In the code above, the render object from the React testing library is imported to render our React.js components. The screen object is also imported, which gives us access to the page document. And the extend-expect module is imported from @testing-library/jest-dom for our assertions. Finally, the component we will be testing, our index page component found in /pages/index.js, is imported.

A test is then included that renders the homepage component (<Home />) exported in the /pages/index.js file, and checks to see if the Get started by editing text that displays on the page is indeed present in the component.

Run this test by running the following command:

npm run test

Tests Local Run 1

Go ahead and add another test by adding the code below to __tests__/HomeTest.js:

...
it("Renders appropriately", () => {
  render(<Home />);
  expect(
    screen.getByRole("heading", { name: "Welcome to Next.js!" })
  ).toBeInTheDocument();
});

The above test uses the screen object to access the React.js DOM and asserts a heading that contains the text Welcome to Next.js!. This is the first item on our homepage.

Run the test suite again

npm run test

Tests Local Run 2

Automating tests for continuous integration

Now that you have your tests running, things are good. But we are not here for just “good.” Let’s use continuous integration to automate the running of our tests. With automation, our tests will run every time we push updates to our repository.

Begin by pushing your project to GitHub.

Next, go to the Add Projects page on the CircleCI dashboard.

Add Project - CircleCI

Click Set Up Project.

Add Config - CircleCI

On the setup page, click Use Existing Config. Next, you’ll get a prompt to either download a configuration file for the CI pipeline or to start building.

Build Prompt - CircleCI

Click Start Building to begin the build. This build will fail because we have not set up our configuration file yet. That is our next step.

Create a folder named .circleci at the root of the project and add a configuration file named config.yml. In this file, enter the following code:

version: 2.1
jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: circleci/node:10.16.3
    steps:
      - checkout
      - run:
          name: Update NPM
          command: "sudo npm install -g npm@5"
      - restore_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
      - run:
          name: Install Dependencies
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules
      - run:
          name: Run tests
          command: npm run test

In the pipeline configuration code above, we first import a Docker image with the required Node.js version for running Next.js applications. Then, npm is updated and dependencies installed, caching them for subsequent builds to be faster.

Lastly, all tests contained in the project are run with the npm run test command.

Save this file and commit all your changes to your remote repository. This will trigger the pipeline to once again run our build script.

Build Success - CircleCI

To view the test details, click build.

Build Results - CircleCI

Awesome, isn’t it?

Conclusion: reducing the burden of boiler plate code

The full project can be seen here on GitHub.

Next.js is quite an impressive framework for building production applications, as it provides features that reduce the burden of boilerplate code. One thing you don’t want in production is broken code. In this tutorial, you learned how to test your features and ensure that the tests run automatically every time you push new code. This goes a long way in making sure that defective code is not pushed to any deployment environment.

Happy coding!


Fikayo Adepoju is a LinkedIn Learning (Lynda.com) Author, Full-stack developer, technical writer, and tech content creator proficient in Web and Mobile technologies and DevOps with over 10 years experience developing scalable distributed applications. With over 40 articles written for CircleCI, Twilio, Auth0, and The New Stack blogs, and also on his personal Medium page, he loves to share his knowledge to as many developers as would benefit from it. You can also check out his video courses on Udemy.

Read more posts by Fikayo Adepoju