Aus technischer Sicht: Testen oder nicht testen

Legen Sie fest, was getestet werden muss und was ausgeschlossen werden kann.

Im vorherigen Artikel wurden die Grundlagen von Testläufen und deren Inhalt behandelt. In diesem Artikel wird die Erstellung von Testläufen aus technischer Perspektive beschrieben. Dabei wird genau erläutert, was in jedem Test enthalten sein sollte und was vermieden werden sollte. Im Wesentlichen erhalten Sie die Antwort auf die jahrelangen Fragen „Was sollte getestet werden?“ oder „Was sollte nicht getestet werden?“.

Was getestet werden soll und was nicht.

Allgemeine Richtlinien und Muster

Es ist zu beachten, dass spezifische Muster und Punkte entscheidend sind, unabhängig davon, ob Sie Einheiten-, Integrations- oder End-to-End-Tests durchführen. Diese Prinzipien können und sollten auf beide Arten von Tests angewendet werden, daher sind sie ein guter Ausgangspunkt.

Weniger ist oft mehr

Beim Schreiben von Tests ist es einer der wichtigsten Dinge, die einfach zu halten. Es ist wichtig, die Kapazität des Gehirns zu berücksichtigen. Der Hauptproduktionscode beansprucht viel Platz und lässt wenig Platz für zusätzliche Komplexität. Dies gilt insbesondere für Tests.

Wenn weniger Platz verfügbar ist, kannst du mit deinen Tests entspannter sein. Deshalb ist es wichtig, beim Testen der Einfachheit halber den Vorrang zu geben. Die Best Practices für JavaScript-Tests von Yoni Goldberg betonen sogar die Bedeutung der goldenen Regel: Dein Test sollte wie ein Assistent und nicht wie eine komplexe mathematische Formel wirken. Mit anderen Worten: Sie sollten die Absicht Ihres Tests auf einen Blick verstehen können.

Mache keine Tests zu komplex, denn das sollte ihnen nicht so vorkommen.

Achten Sie bei allen Testtypen auf Einfachheit, unabhängig von ihrer Komplexität. Je komplexer ein Test ist, desto wichtiger ist seine Vereinfachung. Eine Möglichkeit, dies zu erreichen, ist ein Flat-Test-Design, bei dem die Tests so einfach wie möglich sind und nur das getestet wird, was erforderlich ist. Das bedeutet, dass jeder Test nur einen Testlauf enthalten sollte und sich der Testlauf auf eine einzelne, bestimmte Funktionalität oder Funktion konzentrieren sollte.

Betrachten Sie das Ganze aus dieser Perspektive: Es sollte leicht erkennbar sein, was beim Lesen eines nicht bestandenen Tests schief gelaufen ist. Deshalb ist es wichtig, Tests einfach und leicht verständlich zu halten. So können Sie auftretende Probleme schnell erkennen und beheben.

Herausfinden, was sich lohnt

Das Design des flachen Tests fördert zudem die Konzentration und trägt dazu bei, dass Ihre Tests aussagekräftig sind. Denken Sie daran: Tests sollten nicht nur dem Zweck der Abdeckung dienen. Sie sollten immer einen Zweck haben.

Testet nicht alles.

Implementierungsdetails nicht testen

Ein häufiges Problem bei Tests besteht darin, dass Tests häufig implementiert werden, um Details zur Implementierung zu testen, z. B. die Verwendung von Selektoren in Komponenten oder End-to-End-Tests. Implementierungsdetails beziehen sich auf Dinge, die Nutzer Ihres Codes normalerweise nicht verwenden, sehen oder überhaupt nicht wissen. Dies kann zu zwei großen Problemen bei Tests führen: falsch negative und falsch positive Ergebnisse.

Falsch negative Ergebnisse treten auf, wenn ein Test fehlschlägt, obwohl der getestete Code korrekt ist. Dies kann passieren, wenn sich die Implementierungsdetails aufgrund einer Refaktorierung des Anwendungscodes ändern. Andererseits treten falsch positive Ergebnisse auf, wenn ein Test bestanden wird, obwohl der getestete Code falsch ist.

Eine Lösung für dieses Problem besteht darin, die verschiedenen Typen von Nutzenden zu berücksichtigen. Endnutzer und Entwickler können sich in ihrem Ansatz unterscheiden und mit dem Code unterschiedlich interagieren. Bei der Planung von Tests ist es wichtig, zu berücksichtigen, was Nutzer sehen oder mit denen sie interagieren, und die Tests nicht von den Details der Implementierung, sondern von diesen Faktoren abhängig zu machen.

Wenn Sie beispielsweise Selektoren auswählen, die weniger anfällig für Änderungen sind, können Tests zuverlässiger werden: Datenattribute anstelle von CSS-Selektoren. Weitere Informationen finden Sie unter Kent C. Artikel von Dodds zu diesem Thema finden Sie weitere Informationen zu diesem Thema. Ein Artikel zu diesem Thema folgt später.

Mocking: Immer die Kontrolle behalten

Mocking ist ein breites Konzept, das bei Unittests und manchmal auch bei Integrationstests verwendet wird. Dabei werden gefälschte Daten oder Komponenten erstellt, um Abhängigkeiten zu simulieren, die die vollständige Kontrolle über die Anwendung haben. Dies ermöglicht isolierte Tests.

Die Verwendung von Modellen in Ihren Tests kann die Vorhersehbarkeit, die Trennung von Bedenken und die Leistung verbessern. Und wenn Sie einen Test durchführen müssen, der menschliche Beteiligung erfordert (z. B. die Ausweisüberprüfung), müssen Sie ihn mithilfe einer Simulation verbergen. Aus all diesen Gründen sind Modelle ein wertvolles Werkzeug, das in Betracht gezogen werden sollte.

Gleichzeitig kann die Simulation die Genauigkeit des Tests beeinträchtigen, da es sich um Simulationen und nicht um tatsächliche User Experiences handelt. Sie müssen also vorsichtig sein, wenn Sie Simulationen und Stubs verwenden.

Solltest du dich an End-to-End-Tests beteiligen?

Im Allgemeinen nicht. Simulationen können jedoch manchmal Leben retten. Deshalb sollten wir es nicht ganz ausschließen.

Stellen Sie sich folgendes Szenario vor: Sie schreiben einen Test für eine Funktion, bei der ein externer Zahlungsdienstleister eingebunden ist. Du befindest dich in einer Sandbox-Umgebung, die vom Anbieter bereitgestellt wurde, was bedeutet, dass keine echten Transaktionen stattfinden. Da die Sandbox nicht funktioniert, schlagen Ihre Tests fehl. Die Fehlerbehebung muss vom Zahlungsdienstleister vorgenommen werden. Sie können einfach warten, bis der Dienstleister das Problem behoben hat.

In diesem Fall kann es vorteilhafter sein, die Abhängigkeit von Diensten zu verringern, die Sie nicht steuern können. Es empfiehlt sich dennoch, Simulationen bei Integrations- oder End-to-End-Tests vorsichtig zu verwenden, da dies das Konfidenzniveau Ihrer Tests verringert.

Testdetails: Empfohlene und zu vermeidende Vorgehensweisen

Alles in allem: Was enthält ein Test? Gibt es Unterschiede zwischen den Testtypen? Sehen wir uns einige spezifische Aspekte genauer an, die auf die wichtigsten Testtypen zugeschnitten sind.

Was gehört zu einem guten Einheitentest?

Für einen idealen und effektiven Einheitentest gilt Folgendes:

  • Konzentrieren Sie sich auf bestimmte Aspekte.
  • Sie arbeiten unabhängig voneinander.
  • Auch kleine Szenarien möglich.
  • Verwenden Sie aussagekräftige Namen.
  • Halte dich gegebenenfalls an das AAA-Muster.
  • Garantierte umfassende Testabdeckung.
Das solltest du tun ✅ Nicht ❌
Halten Sie die Tests so klein wie möglich. Testen Sie eine Sache pro Testlauf. Tests über große Einheiten schreiben.
Halten Sie Tests immer isoliert und simulieren Sie die Dinge, die Sie benötigen, die sich außerhalb Ihrer Einheit befinden. Andere Komponenten oder Dienste einschließen.
Halten Sie Tests unabhängig. Frühere Tests nutzen oder Testdaten teilen
Gehen Sie auf verschiedene Szenarien und Pfade ein. Beschränken Sie sich auf den Happy Path oder auf negative Tests.
Verwenden Sie aussagekräftige Testtitel, damit Sie sofort sehen, worum es bei Ihrem Test geht. Test nur anhand des Funktionsnamens. Das Ergebnis ist nicht aussagekräftig genug: testBuildFoo() oder testGetId().
Strebe eine gute Codeabdeckung oder ein breiteres Spektrum an Testläufen an, insbesondere in dieser Phase. Testen Sie von jeder Klasse bis hin zur Datenbankebene (E/A).

Was gehört zu einem guten Integrationstest?

Ein idealer Integrationstest erfüllt auch einige Kriterien wie Unittests. Sie müssen jedoch einige zusätzliche Punkte berücksichtigen. Ein guter Integrationstest sollte:

  • Interaktionen zwischen Komponenten simulieren.
  • Decken Sie reale Szenarien ab und verwenden Sie Simulationen oder Stubs.
  • Achten Sie auf die Leistung.
Das solltest du tun ✅ Nicht ❌
Testen Sie die Integrationspunkte: Prüfen Sie, ob alle Einheiten reibungslos funktionieren, wenn sie miteinander verknüpft sind. Testen Sie jede Einheit isoliert – dafür sind Unittests.
Praxisbeispiele testen: Verwenden Sie Testdaten, die aus realen Daten abgeleitet wurden. Verwenden Sie sich wiederholende, automatisch generierte Testdaten oder andere Daten, die nicht realen Anwendungsfällen entsprechen.
Verwenden Sie Modelle und Stubs für externe Abhängigkeiten, um die Kontrolle über den gesamten Test zu behalten. Erstellen Sie Abhängigkeiten von Drittanbieterdiensten, z. B. Netzwerkanfragen an externe Dienste.
Führen Sie vor und nach jedem Test eine Bereinigung durch. Vergessen Sie, in Ihren Tests Bereinigungsmaßnahmen zu verwenden, da dies aufgrund fehlender ordnungsgemäßer Testisolation zu Testfehlern oder falsch positiven Ergebnissen führen kann.

Was gehört zu einem guten End-to-End-Test?

Ein umfassender End-to-End-Test sollte:

  • Nutzerinteraktionen replizieren
  • Entscheidende Szenarien
  • Sie können sich über mehrere Ebenen erstrecken.
  • Asynchrone Vorgänge verwalten
  • Überprüfen Sie die Ergebnisse.
  • Berücksichtigen Sie die Leistung.
Das solltest du tun ✅ Nicht ❌
Verwenden Sie API-gesteuerte Tastenkombinationen. Weitere Informationen Verwenden Sie UI-Interaktionen für jeden Schritt, einschließlich des beforeEach-Hooks.
Führen Sie vor jedem Test eine Bereinigung durch. Achten Sie noch mehr auf die Testisolation als bei Unit- und Integrationstests, da hier ein höheres Risiko für Nebenwirkungen besteht. Vergessen Sie nach jedem Test die Bereinigung. Wenn Sie den übrig gebliebenen Zustand, die Daten oder Nebeneffekte nicht bereinigen, wirken sich diese auf andere Tests aus, die später ausgeführt werden.
End-to-End-Tests sind wie Systemtests. Sie müssen also den gesamten Anwendungs-Stack testen. Testen Sie jede Einheit isoliert – dafür sind Unittests.
Verwenden Sie während des Tests nur wenige oder gar keine Simulationen. Überlegen Sie sich gut, ob Sie externe Abhängigkeiten simulieren möchten. Verlassen Sie sich stark auf Simulationen.
Berücksichtigen Sie beispielsweise Leistung und Arbeitslast, indem Sie z. B. große Szenarien im selben Test nicht zu stark testen. Große Workflows ohne Tastenkombinationen abdecken