了解如何在 Next.js 中設定常用測試工具 Cypress、Playwright 和 Jest with React Testing Library。
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": { "dev": "next dev", "build": "next build", "start": "next start", "cypress": "cypress open", }
執行 Cypress 以生成與使用其建議的資料夾結構範例:
npm run cypress
你可以查看生成的範例和 Cypress 文件中的 撰寫你的第一個測試 章節,來幫助你熟悉 Cypress。
假設你有以下兩個 Next.js 頁面:
// pages/index.js import Link from 'next/link' export default function Home() { return ( <nav> <Link href="/about"> About </Link> </nav> ) }
// pages/about.js export default function About() { return ( <div> <h1>About Page</h1> </div> ) }
加入一個測試來檢查你的導航是否正常執行:
// 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') }) })
如果你加入了 "baseUrl": "http://localhost:3000"
到 cypress.json
組態檔案中,則可以直接使用較短的寫法 cy.visit("/")
,而不用完整寫出 cy.visit("http://localhost:3000/")
。
由於 Cypress 是用來測試真正的 Next.js 應用程式,所以執行測試前,我們需要先開啟一個 Next.js 伺服器。我們建議你針對生產環境來進行測試,以更貼近真實情境。
執行 npm run build
與 npm run start
,然後開啟另一個終端機來執行 npm run cypress
指令以啟動 Cypress。
注意: 或者,你可以安裝
start-server-and-test
套件和加入"test": "start-server-and-test start http://localhost:3000 cypress"
到package.json
中,以啟動 Next.js 的生產環境伺服器,並結合 Cypress 使用。請記得在有任何新修改後,重新打包你的應用程式。
你會注意到,到目前為止執行 Cypress 會自動開啟一個瀏覽器,這對 CI 來說不好。如果不想讓他自動開啟,你可以用 cypress run
來以無頭(headless)模式執行 Cypress:
// package.json "scripts": { //... "cypress": "cypress open", "cypress:headless": "cypress run", "e2e": "start-server-and-test start http://localhost:3000 cypress", "e2e:headless": "start-server-and-test start http://localhost:3000 cypress:headless" }
你可以從以下資源中了解更多 Cypress 和 持續整合:
Playwright 是一個測試框架,可讓你使用單個 API 自動化 Chromium、Firefox 和 WebKit。你可以使用它在所有平台上撰寫 端到端 (E2E) 和 整合 測試。
最快的入門方法是使用 create-next-app
和 with-playwright 範例。這將建立一個 Next.js 項目,其中包含設定好的 Playwright。
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": { "dev": "next dev", "build": "next build", "start": "next start", "test:e2e": "playwright test", }
假設你有以下兩個 Next.js 頁面:
// pages/index.js import Link from 'next/link' export default function Home() { return ( <nav> <Link href="/about"> About </Link> </nav> ) }
// pages/about.js export default function About() { return ( <div> <h1>About Page</h1> </div> ) }
加入一個測試來檢查你的導航是否正常執行:
// e2e/example.spec.ts import { test, expect } from '@playwright/test' test('should navigate to the about page', async ({ page }) => { // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) await page.goto('http://localhost:3000/') // Find an element with the text 'About Page' and click on it await page.click('text=About') // The new url should be "/about" (baseURL is used there) await expect(page).toHaveURL('http://localhost:3000/about') // The new page should contain an h1 with "About Page" await expect(page.locator('h1')).toContainText('About Page') })
如果你加入了 "baseURL": "http://localhost:3000"
到 playwright.config.ts
配置檔案中,則可以直接使用較短的寫法 page.goto("/")
,而不用完整寫出 page.goto("http://localhost:3000/")
。
由於 Playwright 是用來測試真正的 Next.js 應用程式,所以執行測試前,我們需要先開啟一個 Next.js 伺服器。我們建議你針對生產環境來進行測試,以更貼近真實情境。
執行 npm run build
與 npm run start
,然後開啟另一個終端機來執行 npm run test:e2e
指令以啟動 Playwright。
注意: 或者你可以使用
webServer
功能來讓 Playwright 啟動開發伺服器並等待它啟動完成。
Playwright 會預設以 無頭模式 來執行測試。要安裝所有 Playwright 依賴套件,請執行 npx playwright install-deps
。
你可以從以下資源中了解更多 Playwright 和持續整合:
Jest 和 React 測試庫經常一起用於 單元測試。你可以透過三種方式在 Next.js 應用程式中開始使用 Jest:
以下將介紹如何設定 Jest:
你可以將 create-next-app
和 with-jest 範例一起使用,以快速開始使用 Jest 和 React 測試庫:
npx create-next-app@latest --example with-jest with-jest-app
自 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.js
檔案,並加入以下內容:
// jest.config.js const nextJest = require('next/jest') const createJestConfig = nextJest({ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment dir: './', }) // Add any custom config to be passed to Jest /** @type {import('jest').Config} */ const customJestConfig = { // Add more setup options before each test is run // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work moduleDirectories: ['node_modules', '<rootDir>/'], testEnvironment: 'jest-environment-jsdom', } // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async module.exports = createJestConfig(customJestConfig)
深入研究 Next.js 後,會發現 next/jest
自動幫你配置了 Jest,包括:
transform
.css
、.module.css
及其 scss 變體)和圖片引入.env
(和所有變體)載入到 process.env
node_modules
.next
next.config.js
,作為啟用 SWC 轉換的標記注意: 為了直接測試環境變數,請在單獨的設定腳本或
jest.config.js
檔案中手動加載它們。請參閱 測試環境變數 章節。
如果你在安裝時選擇手動排除 Rust 編譯器,除了上述套件外,你還需要手動配置 Jest 並安裝 babel-jest
和 identity-obj-proxy
。
以下是在 Next.js 配置 Jest 的建議選項:
// jest.config.js module.exports = { collectCoverage: true, // on node 14.x coverage provider v8 offers good speed and more or less good report coverageProvider: 'v8', collectCoverageFrom: [ '**/*.{js,jsx,ts,tsx}', '!**/*.d.ts', '!**/node_modules/**', '!<rootDir>/out/**', '!<rootDir>/.next/**', '!<rootDir>/*.config.js', '!<rootDir>/coverage/**', ], 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 '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`, // Handle module aliases '^@/components/(.*)$': '<rootDir>/components/$1', }, // Add more setup options before each test is run // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'], testEnvironment: 'jsdom', 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)$', ], }
你可以在 Jest 文件 中了解有關每個配置選項的更多資訊。
處理樣式表和圖片引入
測試時並沒有包含樣式表和圖片,因此引入時可能會導致錯誤,因此我們需要模擬它們。在 __mocks__
資料夾中建立上面配置引用的模擬文件 - fileMock.js
和 styleMock.js
:
// __mocks__/fileMock.js module.exports = { src: '/img.jpg', height: 24, width: 24, blurDataURL: 'data:image/png;base64,imagedata', }
// __mocks__/styleMock.js module.exports = {}
有關處理靜態資源的更多資訊,請參閱 Jest Docs。
可選:使用自訂匹配器擴展 Jest
@testing-library/jest-dom
包括一組方便的 自定義匹配器,例如 .toBeInTheDocument()
可以幫你確認指定的 DOM 有沒有渲染出來,使寫測試更簡單。你可以加入以下選項到 Jest 配置文件,來為每個測試引入自定義匹配器:
// jest.config.js setupFilesAfterEnv: ['<rootDir>/jest.setup.js']
然後,在 jest.setup.js
中,加入以下引入:
// jest.setup.js import '@testing-library/jest-dom/extend-expect'
如果你需要在每次測試之前加入更多設置選項,通常可以將它們加入到上面的 jest.setup.js
文件中。
可選:絕對匯入和模組路徑別名
如果你的專案中使用 模組路徑別名,你需要配置 Jest 來匹配 jsconfig.json
文件中的路徑選項與 jest.config.js
文件中的 moduleNameMapper
選項,以解析匯入。例如:
// tsconfig.json or jsconfig.json { "compilerOptions": { "baseUrl": ".", "paths": { "@/components/*": ["components/*"] } } }
// jest.config.js moduleNameMapper: { '^@/components/(.*)$': '<rootDir>/components/$1', }
將測試腳本加入到 package.json
將監視模式下的 Jest 可執行文件加入到 package.json
腳本中:
"scripts": { "dev": "next dev", "build": "next build", "start": "next start", "test": "jest --watch" }
jest --watch
用途為文件有任何修改時,會自動重新執行測試。有關更多 Jest CLI 選項,請參閱 Jest Docs。
建立你的第一個測試
你的專案現在已準備好執行測試。根據 Jests 的慣例,將測試加入到專案根目錄中的 __tests__
資料夾。
例如,我們可以加入一個測試來檢查 <Home />
元件是否成功呈現標題:
// __tests__/index.test.jsx 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() }) })
或者,加入 快照測試 以追蹤 <Home />
元件有沒有任何的非預期變更:
// __tests__/snapshot.js import { render } from '@testing-library/react' import Home from '../pages/index' it('renders homepage unchanged', () => { const { container } = render(<Home />) expect(container).toMatchSnapshot() })
注意: 測試文件不應包含在 pages 資料夾中,因為 pages 資料夾中的任何文件都被視為路由。
執行你的測試
輸入 npm run test
來執行你的測試。在你的測試通過或失敗後,你會注意到一個互動式 Jest 指令列表,在你要加入更多測試時,這些指令會很有幫助。
延伸閱讀:
Next.js 社群建立了一些實用的套件和文章:
延伸閱讀: