Kodokon kodokon.com

Qualität: Testing Library, StrictMode, Profiler

Teste das Verhalten statt der Implementierung, nutze die Doppelaufrufe von StrictMode und miss die Renders mit dem Profiler.

10 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Die Philosophie der Testing Library passt in einen Satz: Teste, was der Nutzer wahrnimmt, niemals die Implementierungsdetails. Konkret fragst du das DOM über den Accessibility-Baum ab: zuerst getByRole (die Königin der Queries, die nebenbei deine ARIA-Rollen validiert), dann getByLabelText, getByText und getByTestId nur als letzten Ausweg. Für Interaktionen bevorzuge user-event gegenüber fireEvent: Wo fireEvent.change ein isoliertes Event auslöst, reproduziert user.type die vollständige Sequenz (focus, keydown, keypress, input, keyup), genau wie eine echte Tastatur. Die findBy*-Varianten kombinieren eine Query mit einem asynchronen Warten: unverzichtbar nach einem Ladeschritt.

JSX
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('adds a task to the list', async () => {
  const user = userEvent.setup();
  render(<TodoApp />);

  await user.type(
    screen.getByRole('textbox'),
    'Review the report'
  );
  await user.click(
    screen.getByRole('button', { name: /add/i })
  );

  expect(
    await screen.findByText('Review the report')
  ).toBeInTheDocument();
});
Ein Test, der auf das Verhalten fokussiert ist, nicht auf die Implementierung.

StrictMode ist ein Entwicklungswerkzeug ohne jegliche Wirkung in der Produktion. In der Entwicklung ruft es deine Komponentenfunktionen, State-Initialisierer und an setState übergebene Funktionen zweimal auf, um unreine Renders aufzuspüren: Liefern zwei Ausführungen unterschiedliche Ergebnisse, hast du einen versteckten Seiteneffekt. Seit React 18 lässt es außerdem jeden Effekt durch den Zyklus Mount, Unmount, Remount laufen: Ein Effekt, dessen Cleanup nicht perfekt symmetrisch ist (nie gekündigtes Abonnement, nie abgebrochene Anfrage), verrät sich sofort. Es ist eine direkte Vorbereitung auf Features, bei denen React einen Screen unmountet und dann wieder mountet, während sein State erhalten bleibt.

JSX
useEffect(() => {
  const controller = new AbortController();
  fetch('/api/user', { signal: controller.signal })
    .then((res) => res.json())
    .then(setUser)
    .catch(() => {});
  return () => controller.abort();
}, []);
Ein symmetrischer Effekt übersteht das doppelte Mounten von StrictMode.

Zum Messen stellt React die Komponente <Profiler> und ihr grafisches Gegenstück in den DevTools bereit. Ihr onRender-Callback erhält unter anderem phase (mount, update oder nested-update), actualDuration (die Zeit, die tatsächlich für das Rendern des Teilbaums in diesem Commit aufgewendet wurde) und baseDuration (eine Schätzung der Zeit, um den gesamten Teilbaum ohne jede Memoisierung zu rendern). Der Abstand zwischen beiden misst die Wirksamkeit deiner memo und useMemo: Eine actualDuration weit unter baseDuration bedeutet, dass die Memoisierung für dich arbeitet.

JSX
import { Profiler } from 'react';

function App() {
  return (
    <Profiler
      id="Sidebar"
      onRender={(id, phase, actualDuration) => {
        console.log(id, phase, actualDuration);
      }}
    >
      <Sidebar />
    </Profiler>
  );
}
Die echten Render-Kosten eines Teilbaums messen.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Warum mountet, unmountet und remountet StrictMode jeden Effekt in der Entwicklung?
    • Um Netzwerk-Latenz des Servers zu simulieren
    • Um zu prüfen, ob das Cleanup jedes Effekts symmetrisch ist
    • Um Reacts internen Cache zu leeren
    • Um das Batching von Updates zu erzwingen
  2. Zu welcher Testing-Library-Query solltest du zuerst greifen?
    • getByTestId, die stabilste
    • getByClassName, die präziseste
    • getByRole, am Accessibility-Baum ausgerichtet
    • querySelector, die flexibelste
  3. Was stellt baseDuration im Profiler-Callback dar?
    • Nur die Zeit des letzten Commits
    • Die geschätzte Render-Zeit des Teilbaums ohne jede Memoisierung
    • Die seit dem Mounten der Komponente verstrichene Zeit
    • Die durchschnittliche Dauer der letzten zehn Renders