Testing

Beispiele

Erfahren Sie, wie Sie Next.js mit häufig verwendeten Test-Tools einrichten: Cypress, Playwright und Jest mit React Testing Library.

Cypress

Cypress ist ein Test-Runner für End-to-End (E2E) und Komponententests.

Schnellstart

Sie können create-next-app mit dem with-cypress Beispiel verwenden, um schnell loszulegen.

Terminal
npx create-next-app@latest --example with-cypress with-cypress-app

Manuelle Einrichtung

Um mit Cypress zu beginnen, installieren Sie das cypress-Paket:

Terminal
npm install --save-dev cypress

Fügen Sie Cypress zum package.json scripts-Feld hinzu:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "cypress:open": "cypress open"
  }
}

Führen Sie Cypress erstmals aus, um Beispiele mit der empfohlenen Ordnerstruktur zu generieren:

Terminal
npm run cypress:open

Sie können die generierten Beispiele und den Abschnitt Writing Your First Test der Cypress-Dokumentation durchsehen, um sich mit Cypress vertraut zu machen.

Sollte ich E2E- oder Komponententests verwenden?

Die Cypress-Dokumentation enthält eine Anleitung zu den Unterschieden zwischen diesen beiden Testtypen und wann jeder geeignet ist.

Erstellen Ihres ersten Cypress E2E-Tests

Angenommen, die folgenden zwei Next.js-Seiten:

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

export default function Home() {
  return (
    <nav>
      <h1>Homepage</h1>
      <Link href="/about">About</Link>
    </nav>
  )
}
pages/about.js
export default function About() {
  return (
    <div>
      <h1>About Page</h1>
      <Link href="/">Homepage</Link>
    </div>
  )
}

Fügen Sie einen Test hinzu, um zu überprüfen, ob Ihre Navigation korrekt funktioniert:

cypress/e2e/app.cy.js
describe('Navigation', () => {
  it('should navigate to the about page', () => {
    // Start von der Index-Seite
    cy.visit('http://localhost:3000/')

    // Finde einen Link mit einem href-Attribut, das "about" enthält, und klicke darauf
    cy.get('a[href*="about"]').click()

    // Die neue URL sollte "/about" enthalten
    cy.url().should('include', '/about')

    // Die neue Seite sollte ein h1 mit "About page" enthalten
    cy.get('h1').contains('About Page')
  })
})

Sie können cy.visit("/") anstelle von cy.visit("http://localhost:3000/") verwenden, wenn Sie baseUrl: 'http://localhost:3000' zur cypress.config.js Konfigurationsdatei hinzufügen.

Erstellen Ihres ersten Cypress-Komponententests

Komponententests bauen und mounten eine bestimmte Komponente, ohne Ihre gesamte Anwendung bündeln oder einen Server starten zu müssen. Dies ermöglicht performantere Tests, die dennoch visuelles Feedback und die gleiche API wie Cypress E2E-Tests bieten.

Gut zu wissen: Da Komponententests keinen Next.js-Server starten, funktionieren Funktionen wie <Image /> und getServerSideProps, die auf einen verfügbaren Server angewiesen sind, nicht ohne weiteres. Siehe die Cypress Next.js-Dokumentation für Beispiele, wie Sie diese Funktionen in Komponententests zum Laufen bringen.

Angenommen die gleichen Komponenten wie im vorherigen Abschnitt, fügen Sie einen Test hinzu, um zu validieren, dass eine Komponente die erwartete Ausgabe rendert:

pages/about.cy.js
import AboutPage from './about.js'

describe('<AboutPage />', () => {
  it('should render and display expected content', () => {
    // Mounte die React-Komponente für die About-Seite
    cy.mount(<AboutPage />)

    // Die neue Seite sollte ein h1 mit "About page" enthalten
    cy.get('h1').contains('About Page')

    // Validiere, dass ein Link mit der erwarteten URL vorhanden ist
    // Das *Folgen* des Links ist besser für einen E2E-Test geeignet
    cy.get('a[href="/"]').should('be.visible')
  })
})

Ausführen Ihrer Cypress-Tests

E2E-Tests

Da Cypress E2E-Tests eine echte Next.js-Anwendung testen, muss der Next.js-Server vor dem Start von Cypress laufen. Wir empfehlen, Ihre Tests gegen Ihren Produktionscode laufen zu lassen, um das Verhalten Ihrer Anwendung besser zu simulieren.

Führen Sie npm run build und npm run start aus, dann führen Sie npm run cypress -- --e2e in einem anderen Terminalfenster aus, um Cypress zu starten und Ihre E2E-Testsuite auszuführen.

Gut zu wissen: Alternativ können Sie das start-server-and-test-Paket installieren und es zum package.json scripts-Feld hinzufügen: "test": "start-server-and-test start http://localhost:3000 cypress", um den Next.js-Produktionsserver in Verbindung mit Cypress zu starten. Denken Sie daran, Ihre Anwendung nach neuen Änderungen neu zu bauen.

Komponententests

Führen Sie npm run cypress -- --component aus, um Cypress zu starten und Ihre Komponententestsuite auszuführen.

Vorbereitung für Continuous Integration (CI)

Sie haben bemerkt, dass das Ausführen von Cypress bisher einen interaktiven Browser geöffnet hat, was für CI-Umgebungen nicht ideal ist. Sie können Cypress auch headless mit dem Befehl cypress run ausführen:

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"
  }
}

Weitere Informationen zu Cypress und Continuous Integration finden Sie in diesen Ressourcen:

Playwright

Playwright ist ein Testframework, mit dem Sie Chromium, Firefox und WebKit mit einer einzigen API automatisieren können. Sie können es verwenden, um End-to-End (E2E) und Integrationstests auf allen Plattformen zu schreiben.

Schnellstart

Der schnellste Weg, um loszulegen, ist die Verwendung von create-next-app mit dem with-playwright Beispiel. Dadurch wird ein Next.js-Projekt mit bereits eingerichtetem Playwright erstellt.

Terminal
npx create-next-app@latest --example with-playwright with-playwright-app

Manuelle Einrichtung

Sie können auch npm init playwright verwenden, um Playwright zu einem bestehenden NPM-Projekt hinzuzufügen.

Um manuell mit Playwright zu beginnen, installieren Sie das @playwright/test-Paket:

Terminal
npm install --save-dev @playwright/test

Fügen Sie Playwright zum package.json scripts-Feld hinzu:

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

Erstellen Ihres ersten Playwright End-to-End-Tests

Angenommen, die folgenden zwei Next.js-Seiten:

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>
  )
}

Fügen Sie einen Test hinzu, um zu überprüfen, ob Ihre Navigation korrekt funktioniert:

import { test, expect } from '@playwright/test'

test('should navigate to the about page', async ({ page }) => {
  // Start von der Index-Seite (die baseURL wird über webServer in der playwright.config.ts gesetzt)
  await page.goto('http://localhost:3000/')
  // Finde ein Element mit dem Text 'About Page' und klicke darauf
  await page.click('text=About')
  // Die neue URL sollte "/about" sein (baseURL wird dort verwendet)
  await expect(page).toHaveURL('http://localhost:3000/about')
  // Die neue Seite sollte ein h1 mit "About Page" enthalten
  await expect(page.locator('h1')).toContainText('About Page')
})
import { test, expect } from '@playwright/test'

test('should navigate to the about page', async ({ page }) => {
  // Start von der Index-Seite (die baseURL wird über webServer in der playwright.config.ts gesetzt)
  await page.goto('http://localhost:3000/')
  // Finde ein Element mit dem Text 'About Page' und klicke darauf
  await page.click('text=About')
  // Die neue URL sollte "/about" sein (baseURL wird dort verwendet)
  await expect(page).toHaveURL('http://localhost:3000/about')
  // Die neue Seite sollte ein h1 mit "About Page" enthalten
  await expect(page.locator('h1')).toContainText('About Page')
})

Sie können page.goto("/") anstelle von page.goto("http://localhost:3000/") verwenden, wenn Sie "baseURL": "http://localhost:3000" zur playwright.config.ts Konfigurationsdatei hinzufügen.

Ausführen Ihrer Playwright-Tests

Da Playwright eine echte Next.js-Anwendung testet, muss der Next.js-Server vor dem Start von Playwright laufen. Es wird empfohlen, Ihre Tests gegen Ihren Produktionscode laufen zu lassen, um das Verhalten Ihrer Anwendung besser zu simulieren.

Führen Sie npm run build und npm run start aus, dann führen Sie npm run test:e2e in einem anderen Terminalfenster aus, um die Playwright-Tests auszuführen.

Gut zu wissen: Alternativ können Sie die webServer-Funktion verwenden, um Playwright den Entwicklungsserver starten und warten zu lassen, bis er vollständig verfügbar ist.

Ausführen von Playwright auf Continuous Integration (CI)

Playwright führt Ihre Tests standardmäßig im Headless-Modus aus. Um alle Playwright-Abhängigkeiten zu installieren, führen Sie npx playwright install-deps aus.

Weitere Informationen zu Playwright und Continuous Integration finden Sie in diesen Ressourcen:

Jest und React Testing Library

Jest und React Testing Library werden häufig zusammen für Unit-Tests verwendet. Es gibt drei Möglichkeiten, Jest in Ihrer Next.js-Anwendung zu verwenden:

  1. Verwendung eines unserer Schnellstart-Beispiele
  2. Mit dem Next.js Rust Compiler
  3. Mit Babel

Die folgenden Abschnitte erklären, wie Sie Jest mit jeder dieser Optionen einrichten können:

Schnellstart

Sie können create-next-app mit dem with-jest Beispiel verwenden, um schnell mit Jest und React Testing Library loszulegen:

Terminal
npx create-next-app@latest --example with-jest with-jest-app

Jest mit dem Rust Compiler einrichten

Seit der Veröffentlichung von Next.js 12 verfügt Next.js über eine integrierte Konfiguration für Jest.

Um Jest einzurichten, installieren Sie jest, jest-environment-jsdom, @testing-library/react, @testing-library/jest-dom:

Terminal
npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom

Erstellen Sie eine jest.config.mjs-Datei im Stammverzeichnis Ihres Projekts und fügen Sie Folgendes hinzu:

jest.config.mjs
import nextJest from 'next/jest.js'

const createJestConfig = nextJest({
  // Geben Sie den Pfad zu Ihrer Next.js-App an, um next.config.js und .env-Dateien in Ihrer Testumgebung zu laden
  dir: './',
})

// Fügen Sie jegliche benutzerdefinierte Konfiguration hinzu, die an Jest übergeben werden soll
/** @type {import('jest').Config} */
const config = {
  // Fügen Sie weitere Setup-Optionen vor jedem Testlauf hinzu
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],

  testEnvironment: 'jest-environment-jsdom',
}

// createJestConfig wird auf diese Weise exportiert, um sicherzustellen, dass next/jest die Next.js-Konfiguration laden kann, die asynchron ist
export default createJestConfig(config)

Unter der Haube konfiguriert next/jest Jest automatisch für Sie, einschließlich:

  • Einrichten von transform mit SWC
  • Automatisches Mocking von Stylesheets (.css, .module.css und deren scss-Varianten), Bildimporten und next/font
  • Laden von .env (und allen Varianten) in process.env
  • Ignorieren von node_modules für Test-Auflösung und Transforms
  • Ignorieren von .next für Test-Auflösung
  • Laden von next.config.js für Flags, die SWC-Transforms aktivieren

Gut zu wissen: Um Umgebungsvariablen direkt zu testen, laden Sie sie manuell in einem separaten Setup-Skript oder in Ihrer jest.config.js-Datei. Weitere Informationen finden Sie unter Testumgebungsvariablen.

Jest einrichten (mit Babel)

Wenn Sie sich gegen den Rust-Compiler entscheiden, müssen Sie Jest manuell konfigurieren und zusätzlich zu den oben genannten Paketen babel-jest und identity-obj-proxy installieren.

Hier sind die empfohlenen Optionen zur Konfiguration von Jest für Next.js:

jest.config.js
module.exports = {
  collectCoverage: true,
  // unter Node 14.x bietet der Coverage-Provider v8 gute Geschwindigkeit und einen mehr oder weniger guten Bericht
  coverageProvider: 'v8',
  collectCoverageFrom: [
    '**/*.{js,jsx,ts,tsx}',
    '!**/*.d.ts',
    '!**/node_modules/**',
    '!<rootDir>/out/**',
    '!<rootDir>/.next/**',
    '!<rootDir>/*.config.js',
    '!<rootDir>/coverage/**',
  ],
  moduleNameMapper: {
    // Umgang mit CSS-Imports (mit CSS-Modulen)
    // https://jestjs.io/docs/webpack#mocking-css-modules
    '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',

    // Umgang mit CSS-Imports (ohne CSS-Module)
    '^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',

    // Umgang mit Bild-Imports
    // https://jestjs.io/docs/webpack#handling-static-assets
    '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`,

    // Umgang mit Modul-Aliasen
    '^@/components/(.*)$': '<rootDir>/components/$1',
  },
  // Weitere Setup-Optionen vor jedem Testlauf hinzufügen
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
  testEnvironment: 'jsdom',
  transform: {
    // Verwenden Sie babel-jest, um Tests mit dem next/babel-Preset zu transpilieren
    // 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)$',
  ],
}

Weitere Informationen zu jeder Konfigurationsoption finden Sie in den Jest-Dokumenten.

Umgang mit Stylesheets und Bild-Imports

Stylesheets und Bilder werden in den Tests nicht verwendet, aber das Importieren kann Fehler verursachen, daher müssen sie gemockt werden. Erstellen Sie die oben in der Konfiguration referenzierten Mock-Dateien - fileMock.js und styleMock.js - in einem __mocks__-Verzeichnis:

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

Weitere Informationen zum Umgang mit statischen Assets finden Sie in den Jest-Dokumenten.

Optional: Jest mit benutzerdefinierten Matchern erweitern

@testing-library/jest-dom enthält eine Reihe praktischer benutzerdefinierter Matcher wie .toBeInTheDocument(), die das Schreiben von Tests erleichtern. Sie können die benutzerdefinierten Matcher für jeden Test importieren, indem Sie die folgende Option zur Jest-Konfigurationsdatei hinzufügen:

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

Fügen Sie dann in jest.setup.js den folgenden Import hinzu:

jest.setup.js
import '@testing-library/jest-dom'

extend-expect wurde in v6.0 entfernt. Wenn Sie @testing-library/jest-dom vor Version 6 verwenden, müssen Sie stattdessen @testing-library/jest-dom/extend-expect importieren.

Wenn Sie weitere Setup-Optionen vor jedem Test hinzufügen müssen, ist es üblich, diese in der oben genannten jest.setup.js-Datei zu ergänzen.

Optional: Absolute Imports und Modul-Pfad-Aliasen

Wenn Ihr Projekt Modul-Pfad-Aliasen verwendet, müssen Sie Jest so konfigurieren, dass es die Imports auflöst, indem Sie die paths-Option in der jsconfig.json-Datei mit der moduleNameMapper-Option in der jest.config.js-Datei abgleichen. Beispiel:

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

Tests erstellen:

Test-Skript zur package.json hinzufügen

Fügen Sie die Jest-Executable im Watch-Modus zu den package.json-Scripts hinzu:

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

jest --watch führt Tests erneut aus, wenn eine Datei geändert wird. Weitere Jest-CLI-Optionen finden Sie in den Jest-Dokumenten.

Erste Tests erstellen

Ihr Projekt ist jetzt bereit für Testläufe. Folgen Sie der Jest-Konvention, indem Sie Tests im __tests__-Ordner im Stammverzeichnis Ihres Projekts hinzufügen.

Beispielsweise können wir einen Test hinzufügen, um zu überprüfen, ob die <Home />-Komponente erfolgreich eine Überschrift rendert:

__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()
  })
})

Optional können Sie einen Snapshot-Test hinzufügen, um unerwartete Änderungen an Ihrer <Home />-Komponente nachzuverfolgen:

__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()
})

Gut zu wissen: Testdateien sollten nicht im Pages Router enthalten sein, da alle Dateien im Pages Router als Routen betrachtet werden.

Testsuite ausführen

Führen Sie npm run test aus, um Ihre Testsuite zu starten. Nachdem Ihre Tests bestanden oder fehlgeschlagen sind, sehen Sie eine Liste interaktiver Jest-Befehle, die hilfreich sein werden, wenn Sie weitere Tests hinzufügen.

Für weiterführende Lektüre könnten diese Ressourcen hilfreich sein:

Community-Pakete und Beispiele

Die Next.js-Community hat Pakete und Artikel erstellt, die hilfreich sein könnten:

Weitere Informationen zu nächsten Schritten empfehlen wir:

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