Ist es eine gute Idee, für jeden Schritt separate Testmethoden zu haben?


10

Ich teste eine REST-API. Angenommen, es wird eine JSON-Struktur zurückgegeben. Was ist der beste Ansatz zum Testen des Servers? Jeder Testschritt kann nur erfolgreich sein, wenn alle vorherigen erfolgreich waren.

Struktur A: Testen Sie alles auf einmal

- Test method 1:
    - make server request
    - assert http response code was 200
    - assert returned file is not empty
    - assert returned file has valid JSON syntax
    - assert returned JSON contains key X

Dies scheint die beste Lösung zu sein.

Vorteile:

  • Nur eine Serveranforderung
  • Ich teste das Verhalten als Ganzes. "Gibt der Server einen JSON mit dem Schlüssel X zurück?"

Struktur B: Fügen Sie jedem Test nach und nach Asserts hinzu

 - Test method 1:
     - make server request
     - assert http response code was 200
 - Test method 2:
     - make server request
     - assert returned file is not empty
 - Test method 3:
     - make server request
     - assert returned file has valid JSON syntax
 - Test method 4:
     - make server request
     - assert returned JSON contains key X

So habe ich angefangen und war überzeugt, dass dies der richtige Weg sein sollte, da jede Methode nur eine einzige Sache testet und dies zu einer besseren Trennung führt. Aber jetzt denke ich, da dies keine Unit-Tests sind, ist meine Trennung nicht angemessen und ich sollte das Verhalten als Ganzes testen.

Struktur C: Einmalige Anforderung stellen und separate Testmethoden für die zwischengespeicherte Antwort ausführen

- make server request and cache it (allow read-only access)

 - Test method 1:
     - assert http response code was 200 on cached server request
 - Test method 2:
     - assert returned file is not empty on cached server request
 - Test method 3:
     - assert returned file has valid JSON syntax on cached server request
 - Test method 4:
     - assert returned JSON contains key X on cached server request

Vorteile:

  • Keine wiederholte (teure) Serveranfrage
  • Hat immer noch Single-Assert-Testmethoden

Welches ist die sinnvollste Teststruktur?


Bitte hören Sie danach auf, Ihre Frage so zu ändern, dass vorhandene Antworten ungültig werden! Vielen Dank.
Doc Brown

Entschuldigen Sie die Unannehmlichkeiten, aber würden Sie etwas anderes vorschlagen?
Mrplow

Überlegen Sie zunächst zweimal, ob Sie Ihre Frage wirklich auf diese Weise ändern müssen. Wenn Sie wirklich der Meinung sind, dass Sie etwas hinzufügen müssen, das einige Antworten ungültig macht, können Sie alle Autoren über diese Antworten informieren, indem Sie unter ihrer Antwort einen Kommentar hinterlassen und sie fragen, ob sie etwas in ihrem Text ändern oder hinzufügen möchten.
Doc Brown

2
Ich habe tatsächlich angenommen, dass Autoren von Antworten benachrichtigt werden, wenn die Frage geändert wird. Aus diesem Grund wollte ich keine Kommentare mit Off-Topic-Anweisungen spammen. Ich werde die Autoren in Zukunft benachrichtigen. Und danke, dass Sie meine Frage beantwortet haben.
Mrplow

Antworten:


3

Best Practices haben immer einen Zweck, einen Grund dahinter. Es ist immer eine gute Idee, diese Gründe in Ihrem Design zu berücksichtigen - insbesondere, wenn Sie entscheiden möchten, wie und wie schwer es ist, diese Best Practices zu befolgen.

In diesem Fall besteht der Hauptgrund dafür, dass jeder Test zu einer einzigen Sache wird, darin, dass die zweite nicht getestet wird, wenn die erste fehlschlägt. Da zu viele Meinungsmacher es zu verdienen scheinen, alles auf das kleinstmögliche Maß zu brechen und jedes Stück so weit wie möglich aufzublähen, entstand die Idee, dass jeder Test eine einzige Behauptung enthalten sollte.

Folge dem nicht blind. Selbst wenn jeder Test eine Sache testen sollte, sollten Sie dennoch überlegen, wie groß oder klein jede "Sache" sein soll, und um dies zu tun, müssen Sie bedenken, warum jeder Test eine Sache testen soll - um sicherzugehen Ein Fehler in der ersten Sache lässt die zweite Sache nicht ungetestet.

Sie müssen sich also fragen: "Brauche ich diese Garantie hier wirklich?"

Angenommen, im ersten Testfall liegt ein Fehler vor - der HTTP-Antwortcode ist dies nicht 200. Sie beginnen also, den Code zu hacken, herauszufinden, warum Sie nicht den Antwortcode erhalten haben, den Sie haben sollten, und beheben das Problem. Und was jetzt?

  • Wenn Sie den Test erneut manuell ausführen, um zu überprüfen, ob Ihr Fix das Problem behoben hat, sollten Sie auf ein anderes Problem stoßen, das durch den ersten Fehler verborgen wurde.
  • Wenn Sie es nicht manuell ausführen (möglicherweise, weil es zu lange dauert?) Und einfach Ihren Fix pushen und darauf warten, dass der automatisierte Testserver alles ausführt, möchten Sie möglicherweise verschiedene Asserts in verschiedene Tests einfügen. Die Zyklen sind in diesem Fall sehr lang, daher lohnt es sich, möglichst viele Fehler in jedem Zyklus zu entdecken.

Es gibt noch einige Dinge zu beachten:

Abhängigkeiten von Behauptungen

Ich weiß, dass die von Ihnen beschriebenen Tests nur ein Beispiel sind und Ihre tatsächlichen Tests wahrscheinlich komplizierter sind. Was ich also sagen werde, ist möglicherweise nicht so stark gültig wie die tatsächlichen Tests, aber es kann dennoch etwas effektiv sein, so dass Sie Vielleicht möchten Sie es in Betracht ziehen.

Wenn Sie einen REST-Service (oder ein anderes HTTP-Protokoll) haben, der Antworten im JSON-Format zurückgibt, schreiben Sie normalerweise eine einfache Client-Klasse, mit der Sie die REST-Methoden wie reguläre Methoden verwenden können, die reguläre Objekte zurückgeben. Unter der Annahme, dass der Client separate Tests hat, um sicherzustellen, dass es funktioniert, hätte ich die ersten 3 Behauptungen verworfen und nur 4 behalten!

Warum?

  • Die erste Zusicherung ist redundant - die Clientklasse sollte eine Ausnahme auslösen, wenn der HTTP-Antwortcode nicht 200 ist.
  • Die zweite Zusicherung ist redundant. Wenn die Antwort leer ist, ist das Ergebnisobjekt null oder eine andere Darstellung eines leeren Objekts, und Sie müssen nirgendwo den Schlüssel X eingeben.
  • Die dritte Zusicherung ist redundant. Wenn der JSON ungültig ist, wird beim Versuch, ihn zu analysieren, eine Ausnahme angezeigt.

Sie müssen also nicht alle diese Tests ausführen - führen Sie einfach den vierten Test aus. Wenn einer der Fehler auftritt, die die ersten drei zu erkennen versuchen, schlägt der Test mit einer geeigneten Ausnahme fehl, bevor Sie überhaupt die tatsächliche Bestätigung erhalten.

Wie möchten Sie die Berichte erhalten?

Angenommen, Sie erhalten keine E-Mails von einem Testserver, sondern die QS-Abteilung führt die Tests aus und benachrichtigt Sie über fehlgeschlagene Tests.

Jack von QA klopft an deine Tür. Er sagt, dass die erste Testmethode fehlgeschlagen ist und die REST-Methode einen schlechten Antwortcode zurückgegeben hat. Sie danken ihm und suchen nach der Grundursache.

Dann kommt Jen von der Qualitätssicherung und sagt, dass die dritte Testmethode fehlgeschlagen ist - die REST-Methode hat keinen gültigen JSON im Antworttext zurückgegeben. Sie sagen ihr, dass Sie sich diese Methode bereits ansehen, und Sie glauben, dass dasselbe, was dazu geführt hat, dass ein fehlerhafter Exit-Code zurückgegeben wurde, auch dazu geführt hat, dass etwas zurückgegeben wurde, das kein gültiger JSON ist und eher wie ein Exception-Stack-Trace aussieht.

Sie können wieder arbeiten, aber dann kommt Jim von der Qualitätssicherung und sagt, dass die vierte Testmethode fehlgeschlagen ist und die Antwort keinen X-Schlüssel enthält ...

Sie können nicht einmal nach dem Grund suchen, da es schwierig ist, Code anzuzeigen, wenn Sie keinen Computerbildschirm haben. Wenn Jim schnell genug wäre, hätte er rechtzeitig ausweichen können ...

E - Mails von dem Testserver sind leichter zu entlassen, aber immer noch - würden Sie nicht lieber nur mitteilen, ONCE , dass etwas mit dem Testverfahren, und Blick auf den relevanten Testprotokollen selbst falsch ist?


3

Wenn Sie davon ausgehen können, dass sich eine Serveranforderung mit denselben Parametern immer gleich verhält, ist Methode B fast sinnlos. Warum sollten Sie viermal dieselbe Methode aufrufen, um viermal dieselben Antwortdaten zu erhalten, wenn ein Aufruf ausreicht?

Und wenn Sie dies nicht sicher annehmen können und es Teil des Tests machen möchten, ist es möglicherweise besser, den Test A mehrmals auszuführen.

Die einzige hypothetische Situation, in der B einen Nutzen haben könnte, besteht darin, dass in Ihrem Testframework nur explizite Testmethoden ein- und ausgeschaltet werden können und Sie erwarten, dass dies für die einzelnen Schritte Ihres Tests erforderlich ist.

Alternative C scheint A mit dem einen Vorteil zu kombinieren, den ich oben für B erwähnt habe. Wenn Ihr Testframework eine einfache Strukturierung Ihres Codes ohne großen Aufwand über B ermöglicht, ist dies ein praktikabler Ansatz. Dies fügt A jedoch eine zusätzliche Komplexität hinzu, sodass ich es nur verwenden würde, wenn ich jemals die einzelnen Tests ein- und ausschalten möchte, andernfalls das YAGNI-Prinzip anwenden und mich an die einfachste Lösung (A) halten möchte.

TLDR: Beginnen Sie mit A, wenn Sie sicher sind, dass immer alle Asserts in einem Test ausgeführt werden sollen, und überarbeiten Sie C, wenn Sie feststellen, dass Sie die einzelnen Asserts von außen leichter kontrollieren müssen.


0

Vermeiden Sie wie bei jedem Code eine vorzeitige Optimierung. Schreiben Sie zuerst Ihre Tests so, dass sie einfach zu lesen und zu warten sind. Wenn die Tests zu langsam werden, optimieren Sie sie. In Ihrem ziemlich einfachen Beispiel sind A und B beide leicht zu lesen und zu warten. Wählen Sie also das gewünschte aus, bis die Dinge zu langsam (Struktur B) oder zu kompliziert (Struktur A) werden.

Wenn Ihr Server zustandslos ist, können Sie die tatsächliche Antwort mit einer erwarteten Antwort vergleichen, um die gesamte Nachricht auf einmal zu überprüfen. Dies geht natürlich zu Lasten der Lesbarkeit.

Wenn Ihr Server voll ist und Sie mehrere langsame API-Aufrufe ausführen müssen, um den Server für den Test in einen Zustand zu versetzen, müssen Sie einen anderen Ansatz wählen, oder die Ausführung Ihrer Tests kann Minuten dauern. Sie können beispielsweise eine Datenbankaktualisierung ausführen, um Daten in eine Testdatenbank einzufügen, damit Sie ein Objekt schnell in einen geeigneten Status zum Testen bringen können. Der Test ist schnell und lesbar, aber schwieriger zu warten. Alternativ können Sie möglicherweise eine Fassade vor die API schreiben, sodass aus mehreren API-Aufrufen einzelne API-Aufrufe werden, die dem von Ihnen getesteten Geschäftsprozess besser entsprechen.


0

Tests sollten keine gemeinsamen Dinge haben - von Grund auf vermeiden Sie den Einfluss eines Tests auf einen anderen. Auf diese Weise können Sie auch Tests in zufälliger Reihenfolge ausführen.
Der C-Weg sollte also nicht akzeptiert werden.


Fragen Sie sich beim Schreiben von Code (oder vielleicht sogar beim Erstellen von etwas anderem) immer: "Warum gibt es solche Praktiken?"
Warum sagen wir, dass es für alles verschiedene Tests geben sollte?

Es gibt zwei Fälle, in denen Sie dies benötigen:

  1. wenn Sie sich nicht auf "Jeder Testschritt kann nur erfolgreich sein, wenn alle vorherigen erfolgreich waren" verlassen können
  2. wenn Ihre Tests keine beschreibenden Bestätigungsnachrichten enthalten

Es gibt zwei Gründe, warum Sie mit diesen Fällen konfrontiert sind:

  1. "Jeder Testschritt kann nur erfolgreich sein, wenn alle vorherigen erfolgreich waren" ist auf Ihre Produktfunktion wirklich nicht anwendbar
  2. Sie haben aufgrund mangelnder Erfahrung oder Zeit oder überwältigender Produktkomplexität nicht genügend Kenntnisse über das Produkt

Wenn Sie aus irgendeinem Grund nicht mindestens einem dieser Gründe erklären kann zu haben , einfach blind die nehmen Struktur B .


Im anderen Fall (ich hoffe , dass Sie hier) Sie wählen ein .


Sie können diese Frage auch auf der Website zur Softwarequalitätssicherung und zum Testen von Stackexchange stellen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.