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!
Top comments (2)
This guide really does not teach how you mock requests made by
getServerSide
andgetStaticProps
. Hard-coded it works, but once you addapi/routes
or requests to third-party services on those page methods, this fails.Thanks so much for your toturial and for the screenshots especially! Never worked with Cypress before, but that was very informative