Contents

    Guides

    A guide to continuous integration in Next.js

    David Adeneye

    David is a developer & technical writer. When he is not writing code/content, he is reading on how to design and develop good software products.

    Published on

    April 4, 2022
    A guide to continuous integration in Next.js

    Next.js is a React framework for developing single-page applications with several features like hybrid static & server rendering, smart bundling, image optimization, internalization, Zero Config, etc. Continuous Integration is a DevOps practice for automating code changes from multiple contributors into a single software project, after which automated build and software are run.

    This tutorial will demonstrate how to set up a testing framework so you can continuously integrate features into your Next.js applications without breaking them. It will then automatically test any features added to our application and ensure that it does not break.

    What is Continuous Integration

    Continuous Integration (CI) is a software development practice for automating the building and testing of your software. It is a DevOps procedure that enables developers to continuously integrate code that is then automatically built and tested before merging it into the shared repository. Therefore, it is crucial to build, test, and deploy code using CI/CD best practices to deliver high-quality software efficiently.

    These are some of the few best practices of Continuous Integration below:

    • Commit code early and commit often
    • Endeavor to read the documentation carefully
    • Optimize pipeline stages
    • Make build fast and simple
    • Your Test environment should mirror production
    • Keep the build fast

    Setting up your Next.js Project

    To set up your Next.js Project, you need to run the following command below to create a new project folder.

    npx create-next-app next-ci

    The code above will automatically create a new Next.js Application inside a next-ci folder. Next, go into the root folder and run the application once you've completed the process:

    cd next-ci
    npm run dev
    

    This will run the development server at http://localhost:3000. You can load the URL in your browser.

    Screenshot browser view showing where to load URL in the browser

    Setting up Testing Framework with Jest

    The next step is to install and set up Jest for testing. Jest is an easy-to-use JavaScript Testing Framework that focuses on simplicity. Here is a list of packages and utilities we will install to run our tests smoothly:

    Run the following command below to install these packages:

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

    Once you've completed the installation, the next step is to create a .babelrc configuration file that instructs babel-jest to use the custom preset for Next.js. Create the .babelrc file in your project root folder and add the following configuration:

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

    After the setup above, we need to configure jest to do the following:

    • Mock static files and CSS import in our tests
    • Ignore the node_modules folder and the .next build folder
    • Use babel-jest to transpile JavaScript in our tests.        

    Add the following section below inside our package.json file:

    ...
    "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"
       }
    }
    ...

    Also, add the test script to the scripts section inside the package.json file:

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

    Mocking

    The previous section added Jest configurations for instructing Jest to mock CSS file imports and other static files in our tests. We mock out these files since they are not useful during tests. You can see above that we specified two mock files, fileMock.js and styleMock.js, for mocking static and CSS files, respectively.

    Now, we need to create the two files. First, create a __mocks__ folder at the root of the project, then create the two files respectively, fileMock.js and styleMock.js.

    Add the following code inside the fileMock.js:

    module.exports = "placeholder-file";

    Add the following code inside the styleMock.js:

    module.exports = {};

    Using these files, you can safely mock static assets and CSS imports.

    Writing Tests

    Since everything is okay, we can start writing our test. To do that, create a  __tests__  folder at the root of the project folder. Then add a test file named IndexTest.js (you can give your test file any descriptive name). Test files are searched for in this directory by Jest.

    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";
    
    describe('Home', () => {
     it('renders without crashing', () => {
         render(<Home />);
         expect(
           screen.getByRole("heading", { name: "Welcome to Next.js!" })
         ).toBeInTheDocument();
     });
      it("Check for Getting Started Text", () => {
       const { getByText } = render(<Home />);
       expect(getByText("Get started by editing")).toBeInTheDocument();
     });
    });

    If we run the test script with this command npm run test, we will get an error:

    This ReferenceError occurs because of the way Next.js renders its pages. Due to the pre-rendering of every page on the server-side for better SEO and optimization, the document object is undefined, as it is client-side only.

    So because we don’t have any document to start with, the test fails. Fortunately, Jest suggests a solution for the error message.

    Consider using the "jsdom" test environment.

    You can do this by adding the jest-environment string at the top of our IndexTest.js like this below:

    /**
    * @jest-environment jsdom
    */
    import { render, screen } from "@testing-library/react";
    import "@testing-library/jest-dom/extend-expect";
    import Home from "../pages/index";
    
    describe('Home', () => {
     it('renders without crashing', () => {
         render();
         expect(
           screen.getByRole("heading", { name: "Welcome to Next.js!" })
         ).toBeInTheDocument();
     });
      it("Check for Getting Started Text", () => {
       const { getByText } = render();
       expect(getByText("Get started by editing")).toBeInTheDocument();
     });
    });

    With these changes, the tests will pass without a hitch!. We use the render object from the React testing library to render our React.js component in the code above. The Screen Object will give us access to the page document. Also, we imported the extend-expect module from @testing-library/jest-dom for our assertions. Also, the component we will be testing is imported from /pages/index.js.

    We wrote two tests in the code above. In the first test, we access the React.js DOM with the screen object and assert a heading containing the text Welcome to Next.js!. As for the second test, we check to see if the Get started by editing text on the page is available in the component.

    See the tests result below:

    Image of test result from having the Get started by editing text available in the component on the page

    Tests Automation for Continuous Integration

    There are several options for setting up continuous integration in your projects, such as TravisCI, Jenkins, CircleCI, and GitHub Actions. CircleCI is the continuous integration & delivery platform that helps the development team push code rapidly and automate the build, test, and deploy applications quicker and easier on a different platform. According to the documentation, Github Actions makes it easy to automate all your software workflow, now with world-class CI/CD. It helps you build, test and deploy your code right from Github. Make code reviews, branch management, and issue triaging work the way you want.

    Since our test is running perfectly well, let's automate the running of our tests with continuous integration. We can run them every time we push updates to our repository by automating our tests.

    Now, push your project to Github.

    Once you're done, sign in to your CircleCI account. Then, go to the Add Projects page on your CircleCI dashboard.

    Image displaying the CircleCI dashboard

    Click the Set Up Project button beside your next-ci repo.

    Image showing pop up modal to select a config.yml file for next-ci

    It will show a modal box like this above; click on the first option.

    Image displaying another pop up modal box for user to pick sample configs for next-ci

    It will show another modal box; click on the first option: Node.js.

    Image displaying pop up modal for user to select between Use Existing Config or Commit and Run

    On this setup page above, click Use Existing Config. It will show you a modal response to either download a CI pipeline configuration file or start building.

    Image displaying modal response to either download a CI pipeline configuration file or start building

    Choose Start Building to begin the build. You will notice this build failed, and that’s because we have yet to set up our configuration file.

    To do that, 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 below:

    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 imported the Docker image with the required Node.js version required for running Next.js application. After that, we updated npm and installed dependencies. Finally, the npm run test command runs all tests contained in the project.

    Then save the file and commit all your changes to your remote repository. As a result, the pipeline will run the build script once more.

    Image displaying updated dashboard view once the pipeline has ran the build script

    It will show this update above. Click build to view the test details.

    Image displaying a list to view the test details on the CircleCI app

    That’s it, happy coding!

    You can find the supported repo for this tutorial here on GitHub.

    Conclusion

    We hope you enjoyed reading through this tutorial. One of the major advantages of continuous integration is that it helps identify and address bugs more quickly, improves software quality, and reduces the time it takes to validate and update software. We demonstrated how to integrate features into your Next.js application continuously. We also explored how to set up test automation with continuous integration using Github Actions and CircleCI.

    You can also extend your knowledge by building a sample Next.js Application and including all the CI techniques you learned from this tutorial.

    Data-rich bug reports loved by everyone.

    Get visual proof, steps to reproduce and technical logs with one click.

    Make bug reporting 50% faster and 100% less painful

    4.6
    |
    Category leader

    Liked the article? Spread the word

    Put your knowledge to practice

    Try Bird on your next bug - you’ll love it

    Try Bird later, from your desktop

    Bird Call to action parrot
    By clicking “Accept”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.