測試

範例

學習如何設定 Next.js 與常用測試工具:CypressPlaywrightJest 搭配 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 欄位:

package.json
{
  "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 頁面:

pages/index.js
import Link from 'next/link'

export default function Home() {
  return (
    <nav>
      <h1>首頁</h1>
      <Link href="/about">關於</Link>
    </nav>
  )
}
pages/about.js
export default function About() {
  return (
    <div>
      <h1>關於頁面</h1>
      <Link href="/">首頁</Link>
    </div>
  )
}

新增一個測試來檢查您的導航是否正常運作:

cypress/e2e/app.cy.js
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 文件 以了解如何在元件測試中使用這些功能。

假設與前一節相同的元件,新增一個測試來驗證元件是否渲染預期輸出:

pages/about.cy.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 buildnpm 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:

package.json
{
  "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 欄位:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "test:e2e": "playwright test"
  }
}

建立第一個 Playwright 端到端測試

假設有以下兩個 Next.js 頁面:

pages/index.js
import Link from 'next/link'

export default function Home() {
  return (
    <nav>
      <Link href="/about">關於</Link>
    </nav>
  )
}
pages/about.js
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')
})

如果您在 playwright.config.ts 設定檔中新增 "baseURL": "http://localhost:3000",則可以使用 page.goto("/") 代替 page.goto("http://localhost:3000/")

執行 Playwright 測試

由於 Playwright 是測試真實的 Next.js 應用程式,因此需要先啟動 Next.js 伺服器才能開始 Playwright。建議針對生產程式碼執行測試,以更貼近應用程式的實際行為。

執行 npm run buildnpm 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:

  1. 使用我們的 快速開始範例
  2. 使用 Next.js Rust 編譯器
  3. 使用 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,請安裝 jestjest-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 檔案並加入以下內容:

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-jestidentity-obj-proxy

以下是為 Next.js 設定 Jest 的推薦選項:

jest.config.js
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.jsstyleMock.js - 放在 __mocks__ 目錄中:

__mocks__/fileMock.js
module.exports = {
  src: '/img.jpg',
  height: 24,
  width: 24,
  blurDataURL: 'data:image/png;base64,imagedata',
}
__mocks__/styleMock.js
module.exports = {}

有關處理靜態資源的更多資訊,請參閱 Jest 文件

選項:使用自定義匹配器擴展 Jest

@testing-library/jest-dom 包含一組方便的 自定義匹配器 (custom matchers),例如 .toBeInTheDocument(),讓撰寫測試更容易。您可以通過在 Jest 設定檔案中添加以下選項,為每個測試導入自定義匹配器:

jest.config.js
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']

然後,在 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 選項匹配。例如:

tsconfig.json or jsconfig.json
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "@/components/*": ["components/*"]
    }
  }
}
jest.config.js
moduleNameMapper: {
  '^@/components/(.*)$': '<rootDir>/components/$1',
}

建立您的測試:

在 package.json 中添加測試腳本

將 Jest 的可執行檔以監視模式添加到 package.json 的 scripts 中:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "test": "jest --watch"
  }
}

jest --watch 會在檔案變更時重新執行測試。有關更多 Jest CLI 選項,請參閱 Jest 文件

建立您的第一個測試

您的專案現在已準備好執行測試。按照 Jest 的慣例,將測試添加到專案根目錄的 __tests__ 資料夾中。

例如,我們可以添加一個測試來檢查 <Home /> 元件是否成功渲染了一個標題:

__tests__/index.test.js
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 /> 元件的任何意外變更:

__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 Router 中,因為 Pages Router 中的任何檔案都會被視為路由。

執行您的測試套件

執行 npm run test 來運行您的測試套件。在測試通過或失敗後,您會看到一系列互動式 Jest 命令,這些命令在您添加更多測試時會很有幫助。

如需進一步閱讀,您可能會發現以下資源有幫助:

社群套件和範例

Next.js 社群創建了一些您可能會覺得有用的套件和文章:

有關下一步該閱讀什麼的更多資訊,我們推薦:

  • pages/basic-features/environment-variables#test-environment-variables