測試
範例
學習如何設定 Next.js 與常用測試工具:Cypress、Playwright 和 Jest 搭配 React Testing Library。
Cypress
Cypress 是一個用於 端到端測試 (E2E) 和 元件測試 的測試執行器。
快速開始
您可以使用 create-next-app
搭配 with-cypress 範例 快速開始。
npx create-next-app@latest --example with-cypress with-cypress-app
手動設定
要開始使用 Cypress,請安裝 cypress
套件:
npm install --save-dev cypress
將 Cypress 加入 package.json
的 scripts 欄位:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"cypress:open": "cypress open"
}
}
首次執行 Cypress 以生成使用其推薦資料夾結構的範例:
npm run cypress:open
您可以查看生成的範例和 Cypress 文件的 編寫第一個測試 部分,以幫助您熟悉 Cypress。
我應該使用 E2E 還是元件測試?
Cypress 文件包含指南 說明這兩種測試類型的差異以及何時適合使用每種測試。
建立第一個 Cypress E2E 測試
假設有以下兩個 Next.js 頁面:
import Link from 'next/link'
export default function Home() {
return (
<nav>
<h1>首頁</h1>
<Link href="/about">關於</Link>
</nav>
)
}
export default function About() {
return (
<div>
<h1>關於頁面</h1>
<Link href="/">首頁</Link>
</div>
)
}
新增一個測試來檢查您的導航是否正常運作:
describe('導航', () => {
it('應該導航到關於頁面', () => {
// 從首頁開始
cy.visit('http://localhost:3000/')
// 找到包含 "about" 的連結並點擊
cy.get('a[href*="about"]').click()
// 新網址應包含 "/about"
cy.url().should('include', '/about')
// 新頁面應包含標題 "About page"
cy.get('h1').contains('About Page')
})
}
如果您在 cypress.config.js
設定檔中新增 baseUrl: 'http://localhost:3000'
,則可以使用 cy.visit("/")
代替 cy.visit("http://localhost:3000/")
。
建立第一個 Cypress 元件測試
元件測試會建置並掛載特定元件,而無需打包整個應用程式或啟動伺服器。這允許更高效的測試,同時仍提供視覺回饋和與 Cypress E2E 測試相同的 API。
須知:由於元件測試不會啟動 Next.js 伺服器,依賴伺服器的功能如
<Image />
和getServerSideProps
將無法直接使用。請參閱 Cypress Next.js 文件 以了解如何在元件測試中使用這些功能。
假設與前一節相同的元件,新增一個測試來驗證元件是否渲染預期輸出:
import AboutPage from './about.js'
describe('<AboutPage />', () => {
it('應該渲染並顯示預期內容', () => {
// 掛載關於頁面的 React 元件
cy.mount(<AboutPage />)
// 新頁面應包含標題 "About page"
cy.get('h1').contains('About Page')
// 驗證包含預期 URL 的連結存在
// *跟隨* 連結更適合 E2E 測試
cy.get('a[href="/"]').should('be.visible')
})
}
執行 Cypress 測試
E2E 測試
由於 Cypress E2E 測試是測試真實的 Next.js 應用程式,因此需要先啟動 Next.js 伺服器才能開始 Cypress。我們建議針對生產程式碼執行測試,以更貼近應用程式的實際行為。
執行 npm run build
和 npm run start
,然後在另一個終端視窗中執行 npm run cypress -- --e2e
以啟動 Cypress 並執行 E2E 測試套件。
須知:或者,您可以安裝
start-server-and-test
套件並將其加入package.json
的 scripts 欄位:"test": "start-server-and-test start http://localhost:3000 cypress"
以在啟動 Next.js 生產伺服器的同時執行 Cypress。記得在變更後重新建置應用程式。
元件測試
執行 npm run cypress -- --component
以啟動 Cypress 並執行元件測試套件。
準備持續整合 (CI)
您可能已經注意到,到目前為止執行 Cypress 會開啟互動式瀏覽器,這在 CI 環境中並不理想。您也可以使用 cypress run
命令以無頭模式執行 Cypress:
{
"scripts": {
//...
"e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"",
"e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"",
"component": "cypress open --component",
"component:headless": "cypress run --component"
}
}
您可以從以下資源了解更多關於 Cypress 和持續整合的資訊:
Playwright
Playwright 是一個測試框架,可讓您使用單一 API 自動化 Chromium、Firefox 和 WebKit。您可以用它來編寫跨平台的 端到端測試 (E2E) 和 整合測試。
快速開始
最快的方法是使用 create-next-app
搭配 with-playwright 範例。這將建立一個已設定好 Playwright 的 Next.js 專案。
npx create-next-app@latest --example with-playwright with-playwright-app
手動設定
您也可以使用 npm init playwright
將 Playwright 加入現有的 NPM
專案。
要手動開始使用 Playwright,請安裝 @playwright/test
套件:
npm install --save-dev @playwright/test
將 Playwright 加入 package.json
的 scripts 欄位:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"test:e2e": "playwright test"
}
}
建立第一個 Playwright 端到端測試
假設有以下兩個 Next.js 頁面:
import Link from 'next/link'
export default function Home() {
return (
<nav>
<Link href="/about">關於</Link>
</nav>
)
}
export default function About() {
return (
<div>
<h1>關於頁面</h1>
</div>
)
}
新增一個測試來驗證您的導航是否正常運作:
import { test, expect } from '@playwright/test'
test('應該導航到關於頁面', async ({ page }) => {
// 從首頁開始 (baseURL 透過 playwright.config.ts 中的 webServer 設定)
await page.goto('http://localhost:3000/')
// 找到文字為 'About Page' 的元素並點擊
await page.click('text=About')
// 新網址應為 "/about" (baseURL 會用於此)
await expect(page).toHaveURL('http://localhost:3000/about')
// 新頁面應包含標題 "About Page"
await expect(page.locator('h1')).toContainText('About Page')
})
import { test, expect } from '@playwright/test'
test('應該導航到關於頁面', async ({ page }) => {
// 從首頁開始 (baseURL 透過 playwright.config.ts 中的 webServer 設定)
await page.goto('http://localhost:3000/')
// 找到文字為 'About Page' 的元素並點擊
await page.click('text=About')
// 新網址應為 "/about" (baseURL 會用於此)
await expect(page).toHaveURL('http://localhost:3000/about')
// 新頁面應包含標題 "About Page"
await expect(page.locator('h1')).toContainText('About Page')
})
如果您在 playwright.config.ts
設定檔中新增 "baseURL": "http://localhost:3000"
,則可以使用 page.goto("/")
代替 page.goto("http://localhost:3000/")
。
執行 Playwright 測試
由於 Playwright 是測試真實的 Next.js 應用程式,因此需要先啟動 Next.js 伺服器才能開始 Playwright。建議針對生產程式碼執行測試,以更貼近應用程式的實際行為。
執行 npm run build
和 npm run start
,然後在另一個終端視窗中執行 npm run test:e2e
以執行 Playwright 測試。
須知:或者,您可以使用
webServer
功能讓 Playwright 啟動開發伺服器並等待其完全可用。
在持續整合 (CI) 上執行 Playwright
Playwright 預設會在 無頭模式 下執行測試。要安裝所有 Playwright 依賴項,請執行 npx playwright install-deps
。
您可以從以下資源了解更多關於 Playwright 和持續整合的資訊:
Jest 和 React Testing Library
Jest 和 React Testing Library 經常一起用於 單元測試。有三種方式可以在 Next.js 應用程式中開始使用 Jest:
- 使用我們的 快速開始範例
- 使用 Next.js Rust 編譯器
- 使用 Babel
以下部分將介紹如何使用這些選項設定 Jest:
快速開始
您可以使用 create-next-app
搭配 with-jest 範例 快速開始使用 Jest 和 React Testing Library:
npx create-next-app@latest --example with-jest with-jest-app
設定 Jest (使用 Rust 編譯器)
自 Next.js 12 發布以來,Next.js 現在內建了 Jest 的設定。
要設定 Jest,請安裝 jest
、jest-environment-jsdom
、@testing-library/react
、@testing-library/jest-dom
:
npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom
在專案的根目錄中建立一個 jest.config.mjs
檔案並加入以下內容:
import nextJest from 'next/jest.js'
const createJestConfig = nextJest({
// 提供 Next.js 應用程式的路徑以在測試環境中載入 next.config.js 和 .env 檔案
dir: './',
})
// 加入任何要傳遞給 Jest 的自訂設定
/** @type {import('jest').Config} */
const config = {
// 在每個測試執行前加入更多設定選項
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testEnvironment: 'jest-environment-jsdom',
}
// 以這種方式匯出 createJestConfig 以確保 next/jest 可以載入非同步的 Next.js 設定
export default createJestConfig(config)
在底層,next/jest
會自動為您設定 Jest,包括:
- 使用 SWC 設定
transform
- 自動模擬樣式表 (
.css
、.module.css
及其 scss 變體)、圖片匯入和next/font
- 將
.env
(及其所有變體) 載入process.env
- 忽略
node_modules
的測試解析和轉換 - 忽略
.next
的測試解析 - 載入
next.config.js
以啟用 SWC 轉換的標誌
須知:要直接測試環境變數,請在單獨的設定腳本或
jest.config.js
檔案中手動載入它們。更多資訊請參閱 測試環境變數。
設定 Jest (搭配 Babel)
如果您選擇不使用 Rust 編譯器 (Rust Compiler),除了上述套件外,您還需要手動設定 Jest 並安裝 babel-jest
和 identity-obj-proxy
。
以下是為 Next.js 設定 Jest 的推薦選項:
module.exports = {
collectCoverage: true,
// 在 node 14.x 上,coverage provider v8 提供良好的速度和還不錯的報告
coverageProvider: 'v8',
collectCoverageFrom: [
'**/*.{js,jsx,ts,tsx}',
'!**/*.d.ts',
'!**/node_modules/**',
'!<rootDir>/out/**',
'!<rootDir>/.next/**',
'!<rootDir>/*.config.js',
'!<rootDir>/coverage/**',
],
moduleNameMapper: {
// 處理 CSS 導入 (使用 CSS modules)
// https://jestjs.io/docs/webpack#mocking-css-modules
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
// 處理 CSS 導入 (不使用 CSS modules)
'^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',
// 處理圖片導入
// https://jestjs.io/docs/webpack#handling-static-assets
'^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`,
// 處理模組別名
'^@/components/(.*)$': '<rootDir>/components/$1',
},
// 在每個測試執行前添加更多設定選項
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
testEnvironment: 'jsdom',
transform: {
// 使用 babel-jest 搭配 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)$',
],
}
您可以在 Jest 文件 中了解更多關於每個設定選項的資訊。
處理樣式表和圖片導入
測試中不會使用樣式表和圖片,但導入它們可能會導致錯誤,因此需要模擬這些檔案。請建立上述設定中引用的模擬檔案 - fileMock.js
和 styleMock.js
- 放在 __mocks__
目錄中:
module.exports = {
src: '/img.jpg',
height: 24,
width: 24,
blurDataURL: 'data:image/png;base64,imagedata',
}
module.exports = {}
有關處理靜態資源的更多資訊,請參閱 Jest 文件。
選項:使用自定義匹配器擴展 Jest
@testing-library/jest-dom
包含一組方便的 自定義匹配器 (custom matchers),例如 .toBeInTheDocument()
,讓撰寫測試更容易。您可以通過在 Jest 設定檔案中添加以下選項,為每個測試導入自定義匹配器:
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']
然後,在 jest.setup.js
中添加以下導入:
import '@testing-library/jest-dom'
extend-expect
已在v6.0
中移除,因此如果您使用的是@testing-library/jest-dom
6.0 之前的版本,則需要導入@testing-library/jest-dom/extend-expect
。
如果您需要在每個測試之前添加更多設定選項,通常會將它們添加到上述的 jest.setup.js
檔案中。
選項:絕對導入和模組路徑別名
如果您的專案使用 模組路徑別名 (Module Path Aliases),您需要設定 Jest 來解析這些導入,方法是將 jsconfig.json
檔案中的 paths
選項與 jest.config.js
檔案中的 moduleNameMapper
選項匹配。例如:
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"@/components/*": ["components/*"]
}
}
}
moduleNameMapper: {
'^@/components/(.*)$': '<rootDir>/components/$1',
}
建立您的測試:
在 package.json 中添加測試腳本
將 Jest 的可執行檔以監視模式添加到 package.json
的 scripts 中:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"test": "jest --watch"
}
}
jest --watch
會在檔案變更時重新執行測試。有關更多 Jest CLI 選項,請參閱 Jest 文件。
建立您的第一個測試
您的專案現在已準備好執行測試。按照 Jest 的慣例,將測試添加到專案根目錄的 __tests__
資料夾中。
例如,我們可以添加一個測試來檢查 <Home />
元件是否成功渲染了一個標題:
import { render, screen } from '@testing-library/react'
import Home from '../pages/index'
import '@testing-library/jest-dom'
describe('Home', () => {
it('renders a heading', () => {
render(<Home />)
const heading = screen.getByRole('heading', {
name: /welcome to next\.js!/i,
})
expect(heading).toBeInTheDocument()
})
})
可選地,添加一個 快照測試 (snapshot test) 來追蹤 <Home />
元件的任何意外變更:
import { render } from '@testing-library/react'
import Home from '../pages/index'
it('renders homepage unchanged', () => {
const { container } = render(<Home />)
expect(container).toMatchSnapshot()
})
小提示:測試檔案不應包含在 Pages Router 中,因為 Pages Router 中的任何檔案都會被視為路由。
執行您的測試套件
執行 npm run test
來運行您的測試套件。在測試通過或失敗後,您會看到一系列互動式 Jest 命令,這些命令在您添加更多測試時會很有幫助。
如需進一步閱讀,您可能會發現以下資源有幫助:
- Jest 文件
- React Testing Library 文件
- Testing Playground - 使用良好的測試實踐來匹配元素。
社群套件和範例
Next.js 社群創建了一些您可能會覺得有用的套件和文章:
- next-router-mock 用於 Storybook。
- 使用 Cypress 測試 Vercel 部署預覽 由 Gleb Bahmutov 撰寫。
有關下一步該閱讀什麼的更多資訊,我們推薦:
- pages/basic-features/environment-variables#test-environment-variables