Wie teste ich Code, der von komplexen APIs abhängt (z. B. Amazon S3)?

13

Ich habe Probleme beim Testen einer Methode, mit der Dokumente auf Amazon S3 hochgeladen werden, aber ich denke, diese Frage gilt für alle nicht trivialen APIs / externen Abhängigkeiten. Ich habe nur drei mögliche Lösungen gefunden, aber keine scheint zufriedenstellend zu sein:

  1. Führen Sie den Code aus, laden Sie das Dokument tatsächlich hoch, überprüfen Sie mit der AWS-API, ob es hochgeladen wurde, und löschen Sie es am Ende des Tests. Dies macht den Test sehr langsam, kostet jedes Mal Geld, wenn der Test ausgeführt wird, und liefert nicht immer das gleiche Ergebnis.

  2. Mock S3. Das ist super haarig, weil ich keine Ahnung habe, was das Innere des Objekts ist, und es fühlt sich falsch an, weil es viel zu kompliziert ist.

  3. Stellen Sie einfach sicher, dass MyObject.upload () mit den richtigen Argumenten aufgerufen wird, und vertrauen Sie darauf, dass ich das S3-Objekt richtig verwende. Das stört mich, weil ich allein aufgrund der Tests nicht sicher sein kann, ob ich die S3-API richtig verwendet habe.

Ich habe überprüft, wie Amazon ihr eigenes SDK testet und sie machen sich über alles lustig. Sie haben einen 200-Zeilen-Helfer, der die Verspottung macht. Ich finde es nicht praktisch für mich, dasselbe zu tun.

Wie löse ich das?

gefedert
quelle
Keine Antwort, aber in der Praxis verwenden wir die drei von Ihnen offen gelegten Ansätze.
Jlhonora

Antworten:

28

Es gibt zwei Themen, die wir hier betrachten müssen.

Das erste ist, dass Sie alle Ihre Tests aus der Sicht des Komponententests betrachten. Unit-Tests sind äußerst wertvoll, aber nicht die einzigen Arten von Tests. Die Tests können in verschiedene Ebenen unterteilt werden, von sehr schnellen Unit-Tests über weniger schnelle Integrationstests bis hin zu noch langsameren Akzeptanztests . (Es können noch mehr Schichten wie Funktionstests ausgebrochen werden .)

Der zweite Grund ist, dass Sie Aufrufe an Code von Drittanbietern mit Ihrer Geschäftslogik mischen, Testherausforderungen erstellen und möglicherweise den Code spröder machen.

Unit-Tests sollten schnell und häufig durchgeführt werden. Das Verspotten von Abhängigkeiten trägt dazu bei, dass diese Tests weiterhin schnell ausgeführt werden, kann jedoch potenziell zu Lücken in der Abdeckung führen, wenn sich die Abhängigkeit ändert und das Verspotten nicht. Ihr Code könnte beschädigt werden, während Ihre Tests noch grün laufen. Einige spöttische Bibliotheken benachrichtigen Sie, wenn sich die Schnittstelle der Abhängigkeit ändert, andere nicht.

Integrationstests hingegen dienen zum Testen der Interaktionen zwischen Komponenten, einschließlich Bibliotheken von Drittanbietern. Mocks sollten auf dieser Teststufe nicht verwendet werden, da wir sehen möchten, wie das eigentliche Objekt zusammenwirkt. Da wir reale Objekte verwenden, sind diese Tests langsamer und werden nicht annähernd so oft ausgeführt wie unsere Komponententests.

Abnahmeprüfungen werden auf einer noch höheren Ebene durchgeführt, um zu überprüfen, ob die Anforderungen an die Software erfüllt sind. Diese Tests werden für das gesamte System ausgeführt, das bereitgestellt werden würde. Auch hier sollte kein Spott angewendet werden.

Eine Richtlinie, die Menschen in Bezug auf Verspottungen für wertvoll befunden haben, ist, keine Typen zu verspotten, die Sie nicht besitzen . Amazon besitzt die API für S3, damit sie sicherstellen können, dass sie sich nicht darunter ändert. Sie hingegen haben diese Zusicherungen nicht. Wenn Sie die S3-API in Ihren Tests verspotten, kann dies Ihren Code ändern und beschädigen, während alle Tests grün angezeigt werden. Wie können wir Code testen, der Bibliotheken von Drittanbietern verwendet?

Nun, wir tun es nicht. Wenn wir der Richtlinie folgen, können wir keine Objekte verspotten, die wir nicht besitzen. Aber ... wenn wir unsere direkten Abhängigkeiten besitzen, können wir sie verspotten. Aber wie? Wir erstellen einen eigenen Wrapper für die S3-API. Wir können es so gestalten, dass es der S3-API ähnelt, oder wir können es unseren Anforderungen genauer anpassen (bevorzugt). Wir können es sogar etwas abstrakter machen, sagen wir PersistenceServiceeher ein als ein AmazonS3Bucket. PersistenceServicewäre eine Schnittstelle mit Methoden wie #save(Thing)und #fetch(ThingId), die Arten von Methoden, die wir gerne sehen würden (dies sind Beispiele, vielleicht möchten Sie tatsächlich verschiedene Methoden). Wir können jetzt eine PersistenceServiceum die S3-API herum implementieren (sagen wir a S3PersistenceService) und diese außerhalb unseres aufrufenden Codes einkapseln.

Nun zu dem Code, der die S3-API aufruft. Wir müssen diese Aufrufe durch Aufrufe an ein PersistenceServiceObjekt ersetzen . Wir verwenden Abhängigkeitsinjektion , um unsere PersistenceServicein das Objekt zu übergeben. Es ist wichtig, nicht nach einem S3PersistenceServicezu fragen, sondern nach einem PersistenceService. Dies ermöglicht es uns, die Implementierung während unserer Tests auszutauschen.

Der gesamte Code, der zur direkten Verwendung der S3-API verwendet wurde PersistenceService, verwendet jetzt unser und S3PersistenceServiceruft jetzt alle S3-API-Aufrufe auf. In unseren Tests können wir eine Verspottung durchführen PersistenceService, da wir sie besitzen, und mithilfe der Verspottung sicherstellen, dass unser Code die richtigen Aufrufe ausführt . Aber jetzt bleibt noch zu testen S3PersistenceService. Es hat das gleiche Problem wie zuvor: Wir können es nicht testen, ohne den externen Dienst anzurufen. Also ... wir testen es nicht. Wir könnten die S3-API-Abhängigkeiten verspotten, aber dies würde uns wenig bis gar kein zusätzliches Vertrauen geben. Stattdessen müssen wir es auf einer höheren Ebene testen: Integrationstests.

Das mag etwas beunruhigend klingen, wenn man sagt, wir sollten keinen Teil unseres Codes testen, aber schauen wir uns an, was wir erreicht haben. Wir hatten überall eine Menge Code, den wir nicht testen konnten PersistenceService. Wir haben unser Bibliotheks-Durcheinander von Drittanbietern auf eine einzige Implementierungsklasse beschränkt. Diese Klasse sollte die für die Verwendung der API erforderliche Funktionalität bereitstellen, ist jedoch nicht mit einer externen Geschäftslogik verknüpft. Wenn es einmal geschrieben ist, sollte es daher sehr stabil sein und sich nicht viel ändern. Wir können uns auf langsamere Tests verlassen, die wir nicht so oft ausführen, weil der Code stabil ist.

Der nächste Schritt besteht darin, die Integrationstests für zu schreiben S3PersistenceService. Diese sollten nach Name oder Ordner getrennt werden, damit wir sie getrennt von unseren schnellen Unit-Tests ausführen können. Integrationstests können häufig dieselben Test-Frameworks wie Unit-Tests verwenden, wenn der Code ausreichend informativ ist, sodass wir kein neues Tool erlernen müssen. Der eigentliche Code für den Integrationstest ist der, den Sie für Option 1 schreiben würden.

cbojar
quelle
Die Frage ist, wie Sie die von Ihnen bereitgestellten Integrationstests bzw. e2e-Tests für APIs ausführen. Sie können den PersistenceService aus offensichtlichen Gründen nicht verspotten. Entweder habe ich etwas falsch verstanden oder eine weitere Ebene zwischen der Anwendungs-API und der AWS-API hinzugefügt, was Ihnen nichts weiter bedeutet, als die Durchführung von
Komponententests zu
@ Yerken Wenn ich darüber nachdenke, bin ich mir ziemlich sicher, dass ich noch eine lange Antwort auf diese Frage geben könnte. Das könnte sich für Sie sogar lohnen, da Sie möglicherweise mehr als nur meine Antwort erhalten.
cbojar
4

Sie müssen beides tun.

Das Ausführen, Hochladen und Löschen ist ein Integrationstest. Es ist mit einem externen System verbunden und daher zu erwarten, dass es langsam läuft. Es sollte wahrscheinlich nicht Teil jedes einzelnen Builds sein, den Sie lokal ausführen, aber es sollte Teil eines CI-Builds oder eines nächtlichen Builds sein. Dies gleicht die Langsamkeit dieser Tests aus und bietet dennoch den Wert, dass sie automatisch getestet werden.

Sie brauchen auch Unittests, die schneller laufen. Da es im Allgemeinen klug ist, sich nicht zu sehr auf ein externes System zu verlassen (so dass Sie Implementierungen austauschen oder umstellen können), sollten Sie wahrscheinlich versuchen, eine einfache Schnittstelle über S3 zu schreiben, gegen die Sie programmieren können. Verspotten Sie diese Schnittstelle in Unittests, damit Sie schnell laufende Unittests haben können.

Bei den ersten Tests wird überprüft, ob Ihr Code tatsächlich mit S3 funktioniert. Bei den zweiten Tests wird überprüft, ob Ihr Code den mit S3 kommunizierenden Code korrekt aufruft.

JDT
quelle
2

Ich würde sagen, dass es von der Komplexität Ihrer Nutzung der API abhängt .

  1. Sie müssen auf jeden Fall mindestens einige Tests durchführen, die die S3-API tatsächlich aufrufen und bestätigen, dass sie von Ende zu Ende funktioniert hat.

  2. Sie müssen auf jeden Fall zusätzliche Tests durchführen, bei denen die API nicht aufgerufen wird, sodass Sie Ihre eigene Software angemessen testen können, ohne die API die ganze Zeit aufzurufen.

Die Frage, die bleibt, ist: Müssen Sie die API verspotten?

Und ich denke, das hängt davon ab, wie viel du damit machst. Wenn Sie nur eine oder zwei einfache Aktionen ausführen, müssen Sie sich meines Erachtens nicht die Mühe eines Modells machen. Ich würde mich damit zufrieden geben, nur meine Nutzung der Funktionen zu überprüfen und einige Live-Tests durchzuführen.

Wenn Ihre Verwendung jedoch komplexer ist, mit verschiedenen Szenarien und verschiedenen Variablen, die sich auf die Ergebnisse auswirken können, müssen Sie sich wahrscheinlich darüber lustig machen, um gründlichere Tests durchzuführen.


quelle
1

Neben den vorherigen Antworten lautet die Hauptfrage, ob (und wie) Sie die S3-API für Ihre Tests verspotten möchten.

Anstatt einzelne S3-Antworten manuell zu verspotten, können Sie einige sehr ausgefeilte vorhandene Verspottungs-Frameworks nutzen. Beispielsweise bietet moto eine Funktionalität, die der tatsächlichen S3-API sehr ähnlich ist.

Sie können sich auch LocalStack ansehen , ein Framework, das vorhandene Tools kombiniert und eine voll funktionsfähige lokale Cloud-Umgebung (einschließlich S3) bietet, die Integrationstests erleichtert.

Obwohl einige dieser Tools in anderen Sprachen (Python) geschrieben sind, sollte es einfach sein, die Testumgebung in einem externen Prozess aus Ihren Tests in Java / JUnit herauszuholen.

whummer
quelle