How to create integration test with Cypress in your Next.js project

In this tutorial we'll describe how you can use Cypress to test your Next.js application, so that you can be more confident when writing or refactoring code.

What is Cypress ?

Cypress is a Javascript end-to-end test framework. It allows you to test your application directly in the browser, with React it's a perfect match because you can test each of your component in the browser and visually see what's wrong and where.
You can learn more about it and how it works here

Generate a Next.js app

npx create-next-app
or
yarn create next-app
and follow the instructions

If you prefer to use Typescript

npx create-next-app --typescript
or
yarn create next-app --typescript

Install Cypress

npm install cypress --save-dev or yarn add cypress -D

In your package.json add a script line

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "cypress:open": "cypress open"
  },

If you use Typescript make sure you add cypress types in your tsconfig.json file

"compilerOptions": {
    ...
    "types": ["cypress"]
  },

Run Cypress

npm run cypress:open or yarn cypress:open will run Cypress for the first time. It will generate automatically every necessary files for you and tests examples in a cypress folder at the root of your project, and open a dedicated page in your browser

You can try running the integration tests examples

These files can be found in your cypress/integration folder. You can delete all of them as we'll write our own one.

Write your first integration test

Under cypress/integration folder create a home.spec.js file (or home.spec.tsx if you use typescript) and add

/// <reference types="cypress"/>

context("Home Page", () => {
  beforeEach(() => {
    cy.visit("http://localhost:3000");
  });

  it("should render the home page and display a message", () => {
    cy.get("h1").contains("Welcome");
  });
});

If you use Typescript add export {} to bypass Eslint compilation error

/// <reference types="cypress"/>

context("Home Page", () => {
  beforeEach(() => {
    cy.visit("http://localhost:3000");
  });

  it("should render the home page and display a message", () => {
    cy.get("h1").contains("Welcome");
  });
});

export {}

Here we are telling Cypress each time it runs the test it should first navigate to the homepage, and search for a <h1> tag containing "Welcome"

If your run this test now it will fail and that's normal

To make it work we need to run our server first and launch the tests, to automate this behavior we will install a library called start-server-and-test

npm install start-server-and-test --save-dev
or
yarn add start-server-and-test -D

Add a script in your package.json

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "cypress:open": "cypress open",
    "test": "start-server-and-test dev 3000 cypress:open"
  },

You can now run
npm run test
or
yarn test

To execute your test and it should work

I will make the test fail again by replacing the word "Welcome" by "Bonjour" and Cypress automatically detect where the test has failed

Now that you understand how Cypress is testing your component you can write your own tests based on what you want to test, and enjoy the visual regression tool in the browser embedded by Cypress 🥳

How to test getStaticProps

To show how we can perform Cypress test on props coming from getStaticProps I have created a file called stack.js under pages folder

// stack.js

const Stack = (props) => {
  const favorites = props.stack;

  return (
    <div style={{display: 'flex', justifyContent: 'center', padding: '2rem'}}>
      <main>
        <h1>My Favorites Stack</h1>
        <ul>
          {favorites.map((favorite) => {
            return <li key={favorite}>{favorite}</li>;
          })}
        </ul>
      </main>
    </div>
  );
};

export default Stack;

export async function getStaticProps() {
  const favoriteStack = [
    'Javascript',
    'TypeScript',
    'React.js',
    'Next.js',
    'GraphQL',
    'Amazon Web Services',
    'Firebase',
  ];

  return {
    props: {
      stack: favoriteStack
    }
  }
}

The goal is to make sure that we receive the correct props, so
we can create a stack.spec.js

/// <reference types="cypress"/>

const favoriteStack = [
  'Javascript',
  'TypeScript',
  'React.js',
  'Next.js',
  'GraphQL',
  'Amazon Web Services',
  'Firebase',
];

context('Stack', () => {
  beforeEach(() => {
    cy.visit('http://localhost:3000/stack');
  });

  it('should render the stack page and display the favorite stack', () => {
    cy.get('ul>li').each((item, index) => {
      cy.wrap(item).should('contain.text', favoriteStack[index]);
    });
  });
});

Here the test should pass because we are receiving the exact same props that we are passing in our component

You can make the test fails by modifying any of the array item and Cypress should show you where it failed, for example here I expect to find "MongoDB" value instead of "Firebase"

How to test getServerSideProps

It should work the same way as for getStaticProps. To demonstrate this we create a new file under pages platforms.js

const Platforms = (props) => {
  const favorites = props.stack;

  return (
    <div style={{display: 'flex', justifyContent: 'center', padding: '2rem'}}>
      <main>
        <h1>My Favorites Freelance platforms</h1>
        <ul>
          {favorites.map((favorite) => {
            return <li key={favorite}>{favorite}</li>;
          })}
        </ul>
      </main>
    </div>
  );
};

export default Platforms;

export async function getServerSideProps() {
  const favoritesPlatforms = [
    'Toptal',
    'Upwork',
    'Malt',
    'Comet'
  ];

  return {
    props: {
      stack: favoritesPlatforms
    }
  }
}

Then we add our test platforms.spec.js

/// <reference types="cypress"/>

const favoritePlatforms = ['Toptal', 'Upwork', 'Malt', 'Comet'];

context('Platforms', () => {
  beforeEach(() => {
    cy.visit('http://localhost:3000/platforms');
  });

  it('should render the platforms page and display the favorite platforms', () => {
    cy.get('ul>li').each((item, index) => {
      cy.wrap(item).should('contain.text', favoritePlatforms[index]);
    });
  });
});

And you can see it's working 🎉

Conclusion

Now you understand how Cypress can work with your Next.js project you can create your own tests based on your scenarios. Enjoy it!

19