New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Docs] Add testing docs #27965
Merged
Merged
[Docs] Add testing docs #27965
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
7668277
[Docs] Add testing page
delbaoliveira 2c12788
Update docs/testing.md
delbaoliveira 2cb4bc9
Update docs/testing.md
delbaoliveira f905c93
Update docs/testing.md
delbaoliveira ab38e7f
Update docs/testing.md
delbaoliveira b598f19
Update docs/testing.md
delbaoliveira e116ec1
Update docs/testing.md
delbaoliveira 043fb18
Update docs/testing.md
delbaoliveira 84bf95b
Update docs/testing.md
delbaoliveira 9be429f
Update docs/testing.md
delbaoliveira d972142
Update docs/testing.md
delbaoliveira c500281
Update docs/testing.md
delbaoliveira eb50af5
Update docs/testing.md
delbaoliveira 7d52e5f
Update docs/testing.md
delbaoliveira b2bf187
Fix linting issue
delbaoliveira bf51e3f
Update docs/testing.md
delbaoliveira dae19af
Add suggested edits
delbaoliveira b8baeff
Merge branch 'canary' into add-testing-docs
kodiakhq[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,335 @@ | ||
# Testing | ||
|
||
<details open> | ||
<summary><b>Examples</b></summary> | ||
<ul> | ||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-jest">Next.js with Jest and React Testing Library</a></li> | ||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-cypress">Next.js with Cypress</a></li> | ||
</ul> | ||
</details> | ||
|
||
Learn how to set up Next.js with three commonly used testing tools: [Jest](https://jestjs.io/docs/tutorial-react), [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/), and [Cypress](https://www.cypress.io/blog/2021/04/06/cypress-component-testing-react/). | ||
|
||
## Jest and React Testing Library | ||
|
||
Jest and React Testing Library are frequently used together for Unit Testing. | ||
|
||
### Quickstart | ||
|
||
You can use `create-next-app` with the [with-jest example](https://github.com/vercel/next.js/tree/canary/examples/with-jest) to quickly get started with Jest and React Testing Library: | ||
|
||
```bash | ||
npx create-next-app --example with-jest with-jest-app | ||
``` | ||
|
||
### Manual setup | ||
|
||
To manually set up Jest and React Testing Library, install `jest` , `@testing-library/react`, `@testing-library/jest-dom` as well as some supporting packages: | ||
|
||
```bash | ||
npm install --save-dev jest babel-jest @testing-library/react @testing-library/jest-dom identity-obj-proxy react-test-renderer | ||
``` | ||
|
||
**Configuring Jest** | ||
|
||
Create a `jest.config.js` file in your project's root directory and add the following configuration options: | ||
|
||
```jsx | ||
// jest.config.js | ||
|
||
module.exports = { | ||
collectCoverageFrom: [ | ||
'**/*.{js,jsx,ts,tsx}', | ||
'!**/*.d.ts', | ||
'!**/node_modules/**', | ||
], | ||
moduleNameMapper: { | ||
// Handle CSS imports (with CSS modules) | ||
// https://jestjs.io/docs/webpack#mocking-css-modules | ||
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', | ||
|
||
// Handle CSS imports (without CSS modules) | ||
'^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js', | ||
|
||
// Handle image imports | ||
// https://jestjs.io/docs/webpack#handling-static-assets | ||
'^.+\\.(jpg|jpeg|png|gif|webp|svg)$': `<rootDir>/__mocks__/fileMock.js`, | ||
}, | ||
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'], | ||
transform: { | ||
// Use babel-jest to transpile tests with the next/babel preset | ||
// https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object | ||
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], | ||
}, | ||
transformIgnorePatterns: [ | ||
'/node_modules/', | ||
'^.+\\.module\\.(css|sass|scss)$', | ||
], | ||
} | ||
``` | ||
|
||
You can learn more about each option above in the [Jest docs](https://jestjs.io/docs/configuration). | ||
|
||
**Handling stylesheets and image imports** | ||
|
||
These files aren't useful in tests but importing them may cause errors, so we will need to mock them. Create the mock files we referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: | ||
|
||
```json | ||
// __mocks__/fileMock.js | ||
|
||
(module.exports = "test-file-stub") | ||
``` | ||
|
||
```json | ||
// __mocks__/styleMock.js | ||
|
||
module.exports = {}; | ||
``` | ||
|
||
For more information on handling static assets, please refer to the [Jest Docs](https://jestjs.io/docs/webpack#handling-static-assets). | ||
|
||
**Extend Jest with custom matchers** | ||
|
||
`@testing-library/jest-dom` includes a set of convenient [custom matchers](https://github.com/testing-library/jest-dom#custom-matchers) such as `.toBeInTheDocument()` making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file: | ||
|
||
```json | ||
// jest.config.js | ||
|
||
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'] | ||
``` | ||
|
||
Then, inside `jest.setup.js`, add the following import: | ||
|
||
```jsx | ||
// jest.setup.js | ||
|
||
import '@testing-library/jest-dom/extend-expect' | ||
``` | ||
|
||
If you need to add more setup options before each test, it's common to add them to the `jest.setup.js` file above. | ||
|
||
**Absolute Imports and Module Path Aliases** | ||
|
||
If your project is using [Module Path Aliases](https://nextjs.org/docs/advanced-features/module-path-aliases), you will need to configure Jest to resolve the imports by matching the paths option in the `jsconfig.json` file with the `moduleNameMapper` option in the `jest.config.js` file. For example: | ||
|
||
```json | ||
// tsconfig.json or jsconfig.json | ||
{ | ||
"compilerOptions": { | ||
"baseUrl": ".", | ||
"paths": { | ||
"@/components/*": ["components/*"] | ||
} | ||
} | ||
} | ||
``` | ||
|
||
```jsx | ||
// jest.config.js | ||
moduleNameMapper: { | ||
'^@/components/(.*)$': '<rootDir>/components/$1', | ||
} | ||
``` | ||
|
||
**Add a test script to package.json** | ||
|
||
Add the Jest executable in watch mode to the `package.json` scripts: | ||
|
||
```jsx | ||
"scripts": { | ||
"dev": "next dev", | ||
"build": "next build", | ||
"start": "next start", | ||
"test": "jest --watch" | ||
} | ||
``` | ||
|
||
`jest --watch` will re-run tests when a file is changed. For more Jest CLI options, please refer to the [Jest Docs](https://jestjs.io/docs/cli#reference). | ||
|
||
**Create your first tests** | ||
|
||
Your project is now ready to run tests. Follow Jests convention by adding tests to the `__tests__` folder in your project's root directory. | ||
|
||
For example, we can add a test to check if the `<Index />` component successfully renders a heading: | ||
|
||
```jsx | ||
// __tests__/testing-library.js | ||
import React from 'react' | ||
import { render } from '@testing-library/react' | ||
import Index from '../pages/index' | ||
|
||
describe('App', () => { | ||
it('renders a heading', () => { | ||
const { getByRole } = render(<Index />) | ||
|
||
const heading = getByRole('heading', { | ||
name: /welcome to next\.js!/i, | ||
}) | ||
|
||
expect(heading).toBeInTheDocument() | ||
}) | ||
}) | ||
``` | ||
|
||
Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes to your `<Index />` component: | ||
|
||
```jsx | ||
// __tests__/snapshot.js | ||
import React from 'react' | ||
import renderer from 'react-test-renderer' | ||
import Index from '../pages/index' | ||
|
||
it('renders homepage unchanged', () => { | ||
const tree = renderer.create(<Index />).toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
``` | ||
|
||
Test files should not be included inside the pages directory because any files inside the pages directory are considered routes. | ||
|
||
**Running your test suite** | ||
|
||
Run `npm run jest` to run your test suite. After your tests pass or fail, you will notice a list of interactive Jest commands that will be helpful as you add more tests. | ||
|
||
For further reading, you may find these resources helpful: | ||
|
||
- [Jest Docs](https://jestjs.io/docs/getting-started) | ||
- [React Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/) | ||
- [Testing Playground](https://testing-playground.com/) - use good testing practices to match elements. | ||
|
||
## Cypress | ||
|
||
Cypress is a test runner used for **End-to-End (E2E)** and **Integration Testing**. | ||
delbaoliveira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Quickstart | ||
|
||
You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-jest) to quickly get started. | ||
|
||
```bash | ||
npx create-next-app --example with-cypress with-cypress-app | ||
``` | ||
|
||
### Manual setup | ||
|
||
To get started with Cypress, install the `cypress` package: | ||
|
||
```bash | ||
npm install --save-dev cypress | ||
``` | ||
|
||
Add Cypress to the `package.json` scripts field: | ||
|
||
```json | ||
"scripts": { | ||
"dev": "next dev", | ||
"build": "next build", | ||
"start": "next start", | ||
"cypress": "cypress open", | ||
} | ||
``` | ||
|
||
Run Cypress for the first time to generate examples that use their recommended folder structure: | ||
|
||
```bash | ||
npm run cypress | ||
``` | ||
|
||
You can look through the generated examples and the [Writing Your First Test](https://docs.cypress.io/guides/getting-started/writing-your-first-test) section of the Cypress Documentation to help you get familiar with Cypress. | ||
|
||
### Creating your first Cypress integration test | ||
|
||
Assuming the following two Next.js pages: | ||
|
||
```jsx | ||
// pages/index.js | ||
import Link from 'next/link' | ||
|
||
export default function Home() { | ||
return ( | ||
<nav> | ||
<Link href="/about"> | ||
<a>About</a> | ||
</Link> | ||
</nav> | ||
) | ||
} | ||
``` | ||
|
||
```jsx | ||
// pages/about.js | ||
export default function About() { | ||
return ( | ||
<div> | ||
<h1>About Page</h1> | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
Add a test to check your navigation is working correctly: | ||
|
||
```jsx | ||
// cypress/integration/app.spec.js | ||
|
||
describe('Navigation', () => { | ||
it('should navigate to the about page', () => { | ||
// Start from the index page | ||
cy.visit('http://localhost:3000/') | ||
|
||
// Find a link with an href attribute containing "about" and click it | ||
cy.get('a[href*="about"]').click() | ||
|
||
// The new url should include "/about" | ||
cy.url().should('include', '/about') | ||
|
||
// The new page should contain an h1 with "About page" | ||
cy.get('h1').contains('About Page') | ||
}) | ||
}) | ||
``` | ||
|
||
You can use `cy.visit("/")` instead of `cy.visit("http://localhost:3000/")` if you add `"baseUrl": "http://localhost:3000"` to the `cypress.json` configuration file. | ||
|
||
### Running your Cypress tests | ||
|
||
Because Cypress is testing a real Next.js application, it requires the Next.js server to be running prior to starting Cypress. Run `npm run dev` to start the development server then run `npm run cypress` in another terminal window to start Cypress. | ||
leerob marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test dev http://localhost:3000 cypress"` to start the Next.js development server when you run Cypress. | ||
|
||
### Getting ready for Continuous Integration (CI) | ||
|
||
You will have noticed that running Cypress so far has opened an interactive browser which is not ideal for CI environments. You can also run Cypress headlessly using the `cypress run` command: | ||
leerob marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```json | ||
// package.json | ||
|
||
"scripts": { | ||
//... | ||
"cypress": "cypress open", | ||
"cypress:headless": "cypress run", | ||
"e2e": "start-server-and-test dev http://localhost:3000 cypress", | ||
"e2e:headless": "start-server-and-test dev http://localhost:3000 cypress:headless" | ||
} | ||
``` | ||
|
||
You can learn more about Cypress and Continuous Integration from these resources: | ||
|
||
- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) | ||
- [Official Cypress Github Action](https://github.com/cypress-io/github-action) | ||
|
||
## Community Packages and Examples | ||
|
||
The Next.js community has created packages and articles you may find helpful: | ||
|
||
- [next-page-tester](https://github.com/toomuchdesign/next-page-tester) for DOM Integration Testing. | ||
- [next-router-mock](https://github.com/scottrippey/next-router-mock) for Storybook. | ||
- [Test Preview Vercel Deploys with Cypress](https://glebbahmutov.com/blog/develop-preview-test/) by Gleb Bahmutov. | ||
|
||
For more information on what to read next, we recommend: | ||
|
||
<div class="card"> | ||
<a href="/docs/basic-features/environment-variables#test-environment-variable.md"> | ||
<b>Test Environment Variables</b> | ||
<small>Learn more test environments.</small> | ||
</a> | ||
</div> |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we mention this? #22740