Die Entwicklung moderner React-Anwendungen mit React Router v7 bringt neue Herausforderungen mit sich – insbesondere beim Testen von Routen mit loader, action und interaktiven Komponenten. Die offiziellen Dokumentationen von React Router empfehlen zwei Ansätze: `createRoutesStub` für Komponententests oder Playwright/Cypress für End-to-End-Tests. Doch beide Methoden haben entscheidende Nachteile.
createRoutesStub eignet sich zwar für einfache Komponenten, versagt aber bei der direkten Prüfung ganzer Routenkomponenten. Die Dokumentation warnt explizit: "Nicht für die direkte Prüfung von Route-Komponenten konzipiert." Playwright und Cypress hingegen erfordern einen separaten Test-Stack, unterbrechen den Entwicklungsworkflow und verzögern das Feedback. Für Entwickler, die eine schnelle und realistische Testumgebung benötigen, ist dies keine ideale Lösung.
Hier kommt TWD ins Spiel – ein Werkzeug, das createRoutesStub in einer echten Browser-Umgebung nutzt. Es kombiniert die Vorteile der offiziellen Methode mit der Realität eines lebendigen DOMs und eines aktiven Entwicklungs-Servers. Die Tests laufen parallel zur Anwendung, ohne zusätzliche Prozesse oder Headless-Browser. Das Ergebnis: eine nahtlose Integration in den Entwicklungsworkflow.
Einrichtung von TWD in einem React-Router-v7-Projekt
Die Integration von TWD erfordert nur wenige Schritte. Beginnen Sie mit der Installation der Abhängigkeiten:
npm install --save-dev twd-js
npx twd-js init public --saveAnschließend konfigurieren Sie die Vite-Plugins in Ihrer vite.config.ts. Achten Sie darauf, sowohl das React-Router- als auch das TWD-Plugin einzubinden:
import { reactRouter } from '@react-router/dev/vite'
import { defineConfig } from 'vite'
import { twd } from 'twd-js/vite-plugin'
export default defineConfig({
plugins: [
reactRouter(),
twd({
testFilePattern: '/**/*.twd.test.{ts,tsx}'
}),
],
})Ein wichtiger Hinweis: React Router v7 im Framework-Modus generiert HTML über app/root.tsx und umgeht dabei den klassischen index.html-Build-Prozess von Vite. Dadurch wird der Hook transformIndexHtml nicht ausgelöst, was die automatische Injektion des TWD-Skripts verhindert. Der Workaround besteht darin, eine bedingte Skript-Einbindung in app/root.tsx hinzuzufügen:
<head>
<Meta />
<Links />
{import.meta.env.DEV && (
<script type="module" src="/@id/virtual:twd/init" />
)}
</head>Diese Zeile stellt sicher, dass das Skript nur in der Entwicklungsphase geladen wird und die Produktion unbeeinflusst bleibt.
Starten Sie die Anwendung mit npm run dev. TWD öffnet automatisch eine Sidebar auf der linken Seite, in der Sie Ihre Tests ausführen können, während die App rechts im echten Browser läuft.
Eine dedizierte Test-Route für stabile Testumgebungen
Um createRoutesStub in der realen Browser-Umgebung nutzen zu können, benötigen Sie einen Container im DOM und eine React-Root-Instanz. Die einfachste Lösung ist die Erstellung einer speziellen Test-Route, die ausschließlich als Mounting-Point dient:
// app/routes/testing-page.tsx
export default function TestPage() {
return (
<div
data-testid="testing-page"
style={{ minHeight: '100vh' }}
/>
)
}Fügen Sie diese Route unter /testing in Ihre Routentabelle ein. Ein kleines Utility-Skript navigiert zu dieser Route und stellt eine neue React-Root-Instanz in jedem beforeEach-Hook bereit:
// app/twd-tests/utils.ts
import { createRoot } from 'react-dom/client'
import { twd, screenDomGlobal } from 'twd-js'
let root: ReturnType<typeof createRoot> | undefined
export async function setupReactRoot() {
if (root) {
root.unmount()
root = undefined
}
await twd.visit('/testing')
const container = await screenDomGlobal.findByTestId('testing-page')
root = createRoot(container)
return root
}Dieses Muster ist konsistent und lässt sich einfach auf alle Testfälle übertragen. Der Aufwand ist minimal, der Nutzen jedoch beträchtlich.
Testen einer Route mit Loader-Daten und Aktionen
Mit TWD können Sie Routenkomponenten mit voller Funktionalität testen – inklusive useLoaderData, useParams und useMatches. Der folgende Beispieltest prüft eine Todo-Liste, die Daten über einen Loader lädt und Aktionen verarbeitet:
// app/twd-tests/todoList.twd.test.tsx
import { twd, expect, userEvent, screenDom } from 'twd-js'
import { describe, it, beforeEach } from 'twd-js/runner'
import { createRoutesStub, useLoaderData, useParams, useMatches } from 'react-router'
import TodoListPage from '~/routes/todolist'
import todoListMock from './mocks/todoList.json'
import { setupReactRoot } from './utils'
describe('Todo List page', () => {
let root: Awaited<ReturnType<typeof setupReactRoot>>
beforeEach(async () => {
root = await setupReactRoot()
})
it('rendert Todos aus dem Loader', async () => {
const Stub = createRoutesStub([
{
path: '/',
Component: () => {
const loaderData = useLoaderData()
const params = useParams()
const matches = useMatches() as any
return (
<TodoListPage
loaderData={loaderData}
params={params}
matches={matches}
/>
)
},
loader() {
return { todos: todoListMock }
},
},
])
root.render(<Stub />)
await twd.wait(300)
const firstTodoTitle = await screenDom.getByText('Learn TWD')
const firstTodoDate = await screenDom.getByText('Date: 2024-12-20')
twd.should(firstTodoTitle, 'be.visible')
twd.should(firstTodoDate, 'be.visible')
})
it('übermittelt die Action mit korrektem Payload', async () => {
let payload: Record<string, string> | null = null
const Stub = createRoutesStub([
{
path: '/todos',
Component: () => null,
loader() {
return { todos: [] }
},
async action({ request }) {
const formData = await request.formData()
payload = Object.fromEntries(formData) as Record<string, string>
return null
},
},
])
root.render(<Stub initialEntries={['/todos']} />)
const titleInput = await screenDom.getByLabelText('Title')
await userEvent.type(titleInput, 'Test Todo')
const submitButton = await screenDom.getByRole('button', { name: 'Create Todo' })
await userEvent.click(submitButton)
expect(payload).to.deep.equal({ title: 'Test Todo' })
})
})Der Test folgt demselben Muster wie die offiziellen React-Router-Dokumentationen. Eine Wrapper-Komponente holt die Daten aus dem Loader und den Routenparametern und übergibt sie als Props an die eigentliche Komponente. Die Interaktionen werden mit userEvent simuliert, während die Assertions auf dem realen DOM basieren. TWD stellt sicher, dass alle Hooks und Framework-Funktionen wie im echten Anwendungsfall funktionieren.
Fazit: Schneller, realistischer und entwicklerfreundlicher Testansatz
TWD schließt die Lücke zwischen einfachen Komponententests und aufwendigen End-to-End-Tests. Durch die Nutzung der offiziellen React-Router-Utilities in einer echten Browser-Umgebung entfällt die Notwendigkeit separater Test-Stacks oder langsamer Feedback-Schleifen. Entwickler können Routen mit Loadern, Actions und komplexen Interaktionen direkt im Entwicklungsprozess testen – mit der gleichen Umgebung, in der auch die Anwendung läuft. Dies beschleunigt nicht nur den Entwicklungsprozess, sondern erhöht auch die Zuverlässigkeit der Tests. Für Teams, die React Router v7 einsetzen, ist TWD eine überzeugende Ergänzung des Testing-Toolkits.
KI-Zusammenfassung
React Router v7 projelerinizdeki loader, action ve form bileşenlerini TWD kullanarak gerçek tarayıcıda ve canlı geliştirme ortamında test edin. Kurulum adımları ve pratik örneklerle hızlı geri bildirim alın.