Communitygithub.com

React-Tests

React-Komponententests mit React Testing Library, Vitest/Jest, MSW für Netzwerk-Mocking, Barrierefreiheitsprüfungen mit axe und die Entscheidungsgrenze zwischen Komponententests und Playwright/Cypress-End-to-End-Tests. Verwenden Sie dies beim Schreiben oder Korrigieren von Tests für React-Komponenten, Hooks oder Seiten.

Was ist React-Tests?

Diese Skill-Dokumentation definiert umfassende Patterns für das Testen von React-Anwendungen mit Fokus auf verhaltensorientierte Komponententests, Tests von Custom Hooks, Barrierefreiheitsprüfungen und netzwerkseitigem Mocking. Sie unterstützt die Test-Runner Vitest und Jest sowie Komponententests in echten Browsern mit Playwright oder Cypress, wenn JSDOM an seine Grenzen stößt. Das Kernprinzip lautet: Testen, was der Benutzer sieht und tut – keine Implementierungsdetails. Die Dokumentation enthält eine Prioritätenliste für Selektoren (Accessible Queries vor Test-IDs), detaillierte Anleitungen zur Benutzerinteraktion mit userEvent (stets asynchron, fireEvent vermeiden), asynchrone Patterns zur Behandlung von Ladezuständen, Einrichtung und Nutzung von MSW für HTTP-Mocking, empfohlene Vorgehensweisen zum Kapseln von Providern in einer test-utils.tsx, sowie Anleitungen zum Testen von Custom Hooks (mit renderHook, act und korrektem QueryClient-Handling) und Barrierefreiheitsprüfungen mit axe. Zusätzlich werden Anti-Patterns aufgezeigt, die Entscheidungsgrenze zu E2E-Tests definiert, Coverage-Ziele pro Schicht empfohlen und der TDD-Workflow beschrieben.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/react-testing

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht React-Tests?

Umfassende Patterns für verhaltensorientierte Komponententests, Custom-Hook-Tests, Barrierefreiheitsprüfungen und netzwerkseitiges Mocking in React.

Wann aktivieren

  • Schreiben von Tests für React-Komponenten, Custom Hooks oder Seiten
  • Hinzufügen von Testabdeckung für bisher ungetestete Komponenten
  • Migration von Enzyme oder Patterns aus der Klassenkomponenten-Ära zu React Testing Library
  • Einrichten von Vitest oder Jest für ein neues React-Projekt
  • Mocken von HTTP-Requests in Tests
  • Prüfen auf Barrierefreiheitsverstöße
  • Entscheidung, welche Tests zu RTL, Playwright Component Testing oder vollständigen E2E-Tests gehören

Kernprinzip

Testen Sie, was der Benutzer sieht und tut, nicht Implementierungsdetails.

Ein Test sollte:

  • Die Komponente mit denselben Providern rendern wie in der Produktion
  • Mit ihr über accessible Queries (role, label) und userEvent interagieren
  • Sichtbare Ausgaben und beobachtbare Seiteneffekte (Callback aufgerufen, Request gesendet) überprüfen

Ein Test sollte NICHT:

  • Den Komponentenstatus, an Kinder übergebene Props oder aufgerufene Hooks inspizieren
  • React selbst oder Framework-Hooks mocken
  • Die Anzahl der Renderings oder die DOM-Struktur über das hinaus prüfen, was den Benutzer betrifft

Bibliotheksauswahl

RunnerWannHinweis
VitestVite, Remix, moderne SetupsSchneller, natives ESM, Jest-kompatible API
JestNext.js, CRA, etablierte ReposStandard für viele React-Projekte
Playwright Component TestingEchter Browser-Engine nötigVerwenden, wenn JSDOM die erforderliche Funktion nicht bietet
Cypress Component TestingEchter Browser, Cypress bereits im EinsatzAlternative zu Playwright CT

Wählen Sie eine aus. Betreiben Sie nicht RTL + Vitest UND Playwright CT im selben Repository, es sei denn, Sie haben eine klare Trennung der Aufgabenbereiche.

Abfragepriorität

React Testing Library bietet Abfragen in drei Ebenen – verwenden Sie diese von oben nach unten:

  1. Für alle zugänglich: getByRole, getByLabelText, getByPlaceholderText, getByText, getByDisplayValue
  2. Semantisch: getByAltText, getByTitle
  3. Test-IDs (Notausstieg): getByTestId
// Am besten
screen.getByRole("button", { name: /save/i });

// OK für Eingabefelder
screen.getByLabelText("Email");

// Letzter Ausweg
screen.getByTestId("save-btn");

Varianten:

  • getBy* — wirft einen Fehler, wenn keine Übereinstimmung
  • queryBy* — gibt null zurück (für "Abwesenheit bestätigen")
  • findBy* — asynchron, gibt ein Promise zurück (für Elemente, die nach asynchroner Arbeit erscheinen)

Benutzerinteraktion mit userEvent

import userEvent from "@testing-library/user-event";

test("sendet das Formular ab", async () => {
  const user = userEvent.setup();
  const onSubmit = vi.fn();
  render(<UserForm onSubmit={onSubmit} />);

  await user.type(screen.getByLabelText("Email"), "[email protected]");
  await user.click(screen.getByRole("button", { name: /save/i }));

  expect(onSubmit).toHaveBeenCalledWith({ email: "[email protected]" });
});
  • userEvent-Aufrufe immer mit await versehen
  • userEvent.setup() einmal pro Test aufrufen, das zurückgegebene user wiederverwenden
  • userEvent simuliert eine echte Browser-Sequenz; fireEvent löst ein einzelnes synthetisches Ereignis aus – bevorzugen Sie userEvent

Asynchrone Patterns

// Element, das nach asynchroner Arbeit erscheint
expect(await screen.findByText("Geladen")).toBeInTheDocument();

// Seiteneffekt-Behauptung
await waitFor(() => expect(saveSpy).toHaveBeenCalled());

// Element, das verschwinden soll
await waitForElementToBeRemoved(() => screen.queryByText("Laden..."));

Verwenden Sie niemals setTimeout + Behauptung — das ist flaky. Nutzen Sie die obigen Matcher.

Netzwerk-Mocking mit MSW

Mock Service Worker mockt auf Netzwerkebene. Die Komponente, Hooks und die Fetch-Bibliothek verhalten sich genau wie in der Produktion.

Einrichtung

// test/setup.ts
import { setupServer } from "msw/node";
import { http, HttpResponse } from "msw";

export const handlers = [
  http.get("/api/users/:id", ({ params }) =>
    HttpResponse.json({ id: params.id, name: "Alice" }),
  ),
  http.post("/api/users", async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ id: "new-id", ...body }, { status: 201 });
  }),
];

export const server = setupServer(...handlers);

beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

Konfigurieren Sie onUnhandledRequest: "error", damit jeder nicht gemockte Request den Test lautstark zum Scheitern bringt – stille Erfolge sind schlimmer als rote Tests.

Überschreibung pro Test

test("zeigt Fehler bei 500", async () => {
  server.use(
    http.get("/api/users/:id", () => new HttpResponse(null, { status: 500 })),
  );
  render(<UserPage id="1" />);
  expect(await screen.findByText(/etwas ist schief gelaufen/i)).toBeInTheDocument();
});

Provider kapseln

Kapseln Sie Provider einmal in einer test-utils.tsx:

// test-utils.tsx
import { render, RenderOptions } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

export function renderWithProviders(
  ui: React.ReactElement,
  options?: RenderOptions,
) {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  return render(
    <QueryClientProvider client={queryClient}>
      <ThemeProvider theme={lightTheme}>
        <MemoryRouter>{ui}</MemoryRouter>
      </ThemeProvider>
    </QueryClientProvider>,
    options,
  );
}

export * from "@testing-library/react";

Dann in jeder Testdatei import { renderWithProviders, screen } from "test-utils" verwenden.

Custom Hooks testen

import { renderHook, act } from "@testing-library/react";

test("useCounter erhöht und verringert", () => {
  const { result } = renderHook(() => useCounter(0));

  expect(result.current.count).toBe(0);

  act(() => result.current.increment());
  expect(result.current.count).toBe(1);

  act(() => result.current.decrement());
  expect(result.current.count).toBe(0);
});

test("useCounter akzeptiert Startwert", () => {
  const { result } = renderHook(() => useCounter(10));
  expect(result.current.count).toBe(10);
});

test("useUser ruft Benutzerdaten ab", async () => {
  // QueryClient EINMAL pro Test außerhalb des Wrappers instanziieren, damit er Neu-Renderings überlebt.
  // Wird er innerhalb der Wrapper-Closure erstellt, setzt er den Cache bei jedem Rendering zurück, was zu flaky Tests führt.
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });
  const wrapper = ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );

  const { result } = renderHook(() => useUser("1"), { wrapper });

  await waitFor(() => expect(result.current.isSuccess).toBe(true));
  expect(result.current.data).toEqual({ id: "1", name: "Alice" });
});
  • Zustandsändernde Aufrufe in act kapseln
  • Nur über die öffentliche API des Hooks testen
  • Für Hooks, die Kontext verwenden, einen wrapper übergeben

Barrierefreiheitsprüfungen

import { axe, toHaveNoViolations } from "jest-axe"; // oder vitest-axe
expect.extend(toHaveNoViolations);

test("UserCard hat keine a11y-Verstöße", async () => {
  const { container } = render(<UserCard user={mockUser} />);
  expect(await axe(container)).toHaveNoViolations();
});

Führen Sie axe in Komponententests für jede interaktive Komponente aus. Erfasst:

  • Fehlende Labels für Formulareingaben
  • Ungültige ARIA-Nutzung
  • Schlechten Farbkontrast (eingeschränkt – JSDOM hat keine echte CSS-Engine, daher funktioniert dies nur für Inline-Stile; visueller Kontrast gehört zu Playwright)
  • Fehlenden Alternativtext bei Bildern
  • Verstöße gegen die Überschriftenreihenfolge

Querverweis: skills/accessibility/SKILL.md für das erweiterte a11y-Test-Handbuch.

Wann KEINE Snapshot-Tests verwenden

Snapshots von gerenderten Ausgaben:

  • Brechen bei jeder Style-Änderung
  • Werden während des Reviews einfach durchgewunken
  • Testen Implementierungsdetails (DOM-Struktur), nicht das Verhalten

Akzeptable Snapshot-Verwendungen:

  • Reine Daten-Serialisierungsfunktionen (formatInvoice(invoice) -> stabiler String)
  • Generierte Konfigurationsdateien (z.B. Ausgabe der Webpack-Konfiguration)

Für visuelle Regressionen bei Komponenten verwenden Sie Playwright/Cypress-Screenshots oder Percy/Chromatic – echte visuelle Unterschiede, keine DOM-Strings.

Wann zu Playwright / Cypress wechseln

JSDOM (von Vitest/Jest verwendet) kann nicht:

  • Echtes Layout rendern (Flexbox, Grid, Viewport-Abfragen)
  • Native Browser-Animationen, CSS-Übergänge ausführen
  • Scroll-Verhalten, Drag-and-Drop, Einfügen aus der Zwischenablage testen
  • Iframes, Popups, Downloads, Cross-Origin-Abläufe handhaben
  • Echtes Netzwerk in einer kontrollierten Umgebung mit vollständiger DevTools-Unterstützung ausführen

Für all das verwenden Sie Playwright Component Testing (Komponententest im echten Browser) oder vollständige E2E-Tests. Siehe e2e-testing skill.

Entscheidungsgrenze:

  • Ein Hook, eine präsentative Komponente, ein Formular mit Logik -> RTL
  • Eine Komponente, deren Layout wichtig ist oder die Browser-APIs verwendet, die nicht in JSDOM vorhanden sind -> Playwright CT
  • Ein vollständiger Benutzerfluss über mehrere Seiten -> Playwright/Cypress E2E

Coverage-Ziele

SchichtZiel
Reine Hilfsfunktionen>=90%
Custom Hooks>=85%
Präsentative Komponenten>=80% — Verhalten, nicht Zeilen
Container-Komponenten>=70% — Happy Paths + Fehlerzustände
SeitenE2E separat abgedeckt; Mindestmaß an Smoke-Tests

Konfigurieren über vitest.config.ts / jest.config.js:

// vitest.config.ts
test: {
  coverage: {
    provider: "v8",
    reporter: ["text", "html", "lcov"],
    thresholds: {
      lines: 80,
      functions: 80,
      branches: 70,
      statements: 80,
    },
  },
}

Anti-Patterns

  • container.querySelector("...") — umgeht accessible Queries, lässt Tests bestehen, wenn echte Benutzer scheitern würden
  • Auf die Anzahl der Renderings beharren — Implementierungsdetail
  • jest.mock("react", ...) — React niemals mocken. Stattdessen die Komponente umgestalten
  • Kind-Komponenten standardmäßig mocken — testet die Integration, nicht die Isolation. Mocken Sie nur, wenn das Kind starke Seiteneffekte hat
  • act()-Warnungen ignorieren — sie weisen auf echte Fehler hin (Statusaktualisierung nach Unmount, fehlende asynchrone Kapselung)
  • Veränderlichen Status zwischen Tests teilen — flaky, wenn sich die Testreihenfolge ändert
  • Tests, die mit entferntem it.skip() bestehen — Ihr Test behauptet nicht das, was Sie denken

TDD-Workflow

ROT    -> Schreiben Sie einen fehlschlagenden Test für die nächste Anforderung
GRÜN   -> Schreiben Sie minimalen Komponentencode, um den Test zu bestehen
UMBAU  -> Verbessern Sie die Komponente, die Tests bleiben grün
WIEDERHOLEN -> Nächste Anforderung

Für neue Komponenten:

  1. Definieren Sie den Prop-Typ und die Signatur der Komponente
  2. Schreiben Sie den ersten Test für den einfachsten Fall
  3. Stellen Sie sicher, dass er aus dem richtigen Grund fehlschlägt
  4. Implementieren Sie gerade genug, um zu bestehen
  5. Fügen Sie den nächsten Testfall hinzu
  6. Bauen Sie um, wenn der dritte ähnliche Test ein Pattern offenbart

Testbefehle

# Vitest
vitest                            # Watch-Modus
vitest run                        # Einmalige Ausführung
vitest run --coverage             # Mit Coverage
vitest run path/to/file.test.tsx  # Einzelne Datei

# Jest
jest --watch
jest --coverage
jest path/to/file.test.tsx

# CI-Modus
CI=true vitest run --coverage

Verwandtes

Beispiele

Formularübermittlung mit MSW und userEvent

test("sendet Benutzerformular ab und zeigt Erfolg", async () => {
  server.use(
    http.post("/api/users", () =>
      HttpResponse.json({ id: "1", name: "Alice" }, { status: 201 }),
    ),
  );

  const user = userEvent.setup();
  renderWithProviders(<UserForm />);

  await user.type(screen.getByLabelText("Name"), "Alice");
  await user.type(screen.getByLabelText("Email"), "[email protected]");
  await user.click(screen.getByRole("button", { name: /save/i }));

  expect(await screen.findByText(/erfolgreich gespeichert/i)).toBeInTheDocument();
});

Testen einer Error Boundary

function Broken() {
  throw new Error("knall");
}

test("Error Boundary rendert Fallback", () => {
  // console.error von React für den erwarteten Fehler unterdrücken, dann wiederherstellen,
  // damit der Spy nicht zwischen Tests durchsickert und echte Fehler anderswo versteckt.
  const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
  try {
    render(
      <ErrorBoundary fallback={<div>Etwas ist schief gelaufen</div>}>
        <Broken />
      </ErrorBoundary>,
    );

    expect(screen.getByText("Etwas ist schief gelaufen")).toBeInTheDocument();
  } finally {
    errorSpy.mockRestore();
  }
});

Testen einer Suspense Boundary

test("zeigt Laden dann Inhalt", async () => {
  renderWithProviders(
    <Suspense fallback={<div>Laden...</div>}>
      <UserDetail id="1" />
    </Suspense>,
  );

  expect(screen.getByText("Laden...")).toBeInTheDocument();
  expect(await screen.findByText("Alice")).toBeInTheDocument();
});

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Verwandte Skills