it-swarm.com.de

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

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 von den Interna dieses Objekts habe und es sich falsch anfühlt, 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 korrekt verwende. Dies stört mich, da ich nicht sicher wissen kann, ob ich die S3-API allein aus den Tests korrekt verwendet habe.

Ich habe überprüft, wie Amazon sein eigenes SDK testet und sie verspotten alles. Sie haben einen 200-Zeilen-Helfer, der die Verspottung macht. Ich halte es nicht für praktisch, dasselbe zu tun.

Wie löse ich das?

13
springloaded

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

Das erste ist, dass Sie scheinbar alle Ihre Tests aus der Perspektive des Komponententests betrachten. Unit-Tests sind äußerst wertvoll, aber nicht die einzigen Arten von Tests. Tests können tatsächlich in mehrere verschiedene Ebenen unterteilt werden, von sehr schnell Komponententests bis weniger schnell Integrationstests bis noch langsamer Abnahmetests . (Es können noch mehr Schichten ausgebrochen sein, wie Funktionstests .)

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

Unit-Tests sollten schnell sein und häufig durchgeführt werden. Das Verspotten von Abhängigkeiten hilft dabei, diese Tests schnell laufen zu lassen, kann jedoch möglicherweise Lücken in der Abdeckung verursachen, wenn sich die Abhängigkeit ändert und das Mock dies nicht tut. Ihr Code könnte beschädigt werden, während Ihre Tests noch grün laufen. Einige Spottbibliotheken benachrichtigen Sie, wenn sich die Benutzeroberfläche der Abhängigkeit ändert, andere nicht.

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

Akzeptanztests sehen noch höher aus und prüfen, ob die Anforderungen an die Software erfüllt sind. Diese Tests werden für das gesamte System ausgeführt, das bereitgestellt werden soll. Auch hier sollte kein Spott verwendet werden.

Eine Richtlinie, die Menschen in Bezug auf Mocks als wertvoll empfunden haben, ist keine Mock-Typen, die Sie nicht besitzen . Amazon besitzt die API für S3, damit sie sicherstellen können, dass sie sich unter ihnen nicht ändert. Auf der anderen Seite haben Sie diese Zusicherungen nicht. Wenn Sie die S3-API in Ihren Tests verspotten, kann dies Ihren Code ändern und beschädigen, während Ihre Tests alle 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 unseren eigenen Wrapper für die S3-API. Wir können dafür sorgen, dass es der S3-API sehr ähnlich sieht, oder wir können es unseren Anforderungen besser anpassen (bevorzugt). Wir können es sogar etwas abstrakter machen, sagen wir ein PersistenceService anstatt eines AmazonS3Bucket. PersistenceService wäre eine Schnittstelle mit Methoden wie #save(Thing) und #fetch(ThingId), den Arten von Methoden, die wir gerne sehen würden (dies sind Beispiele, Sie möchten möglicherweise tatsächlich verschiedene Methoden). . Wir können jetzt ein PersistenceService um die S3-API implementieren (sagen wir ein S3PersistenceService) Und es von unserem aufrufenden Code wegkapseln.

Nun zum Code, der die S3-API aufruft. Wir müssen diese Aufrufe durch Aufrufe eines PersistenceService -Objekts ersetzen. Wir verwenden Abhängigkeitsinjektion , um unser PersistenceService an das Objekt zu übergeben. Es ist wichtig , nicht nach einem S3PersistenceService Zu 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, verwendet jetzt unser PersistenceService, und unser S3PersistenceService Führt jetzt alle Aufrufe der S3-API durch. In unseren Tests können wir PersistenceService verspotten, da wir es besitzen, und das Modell verwenden, um sicherzustellen, dass unser Code die richtigen Aufrufe ausführt. Aber jetzt bleibt das Testen von 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 verspotten die S3-API-Abhängigkeiten, 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.

Dies mag ein wenig beunruhigend klingen, wenn wir sagen, dass wir einen Teil unseres Codes nicht einzeln testen sollten, aber schauen wir uns an, was wir erreicht haben. Wir hatten überall eine Menge Code, den wir nicht testen konnten und der jetzt über PersistenceService Unit-getestet werden kann. Wir haben unser Bibliotheks-Chaos von Drittanbietern auf eine einzelne Implementierungsklasse beschränkt. Diese Klasse sollte die für die Verwendung der API erforderlichen Funktionen bereitstellen, ist jedoch keiner externen Geschäftslogik zugeordnet. Sobald es geschrieben ist, sollte es daher sehr stabil sein und sich nicht sehr ä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 S3PersistenceService Zu schreiben. Diese sollten nach Name oder Ordner getrennt sein, damit wir sie getrennt von unseren schnellen Komponententests ausführen können. Integrationstests können häufig dieselben Testframeworks wie Komponententests 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 Ihre Option 1 schreiben würden.

28
cbojar

Sie müssen beides tun.

Das Ausführen, Hochladen und Löschen ist ein Integrationstest. Es ist mit einem externen System verbunden und kann daher als langsam angesehen werden. 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, sie automatisch testen zu lassen.

Sie benötigen auch Unittests, die schneller ausgeführt werden. Da es im Allgemeinen klug ist, nicht zu sehr von einem externen System abhängig zu sein (damit Sie Implementierungen austauschen oder umschalten können), sollten Sie wahrscheinlich versuchen, eine einfache Schnittstelle über S3 zu schreiben, gegen die Sie codieren können. Verspotten Sie diese Schnittstelle in Unittests, damit Sie schnell laufende Unittests haben können.

Die ersten Tests überprüfen, ob Ihr Code tatsächlich mit S3 funktioniert, die zweiten Tests, ob Ihr Code den Code, der mit S3 kommuniziert, korrekt aufruft.

4
JDT

Ich würde sagen, dass es hängt von der Komplexität Ihrer Verwendung der API ab.

  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, damit Sie Ihre eigene Software angemessen testen können, ohne die API ständig aufrufen zu müssen.

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

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

Wenn Ihre Verwendung jedoch komplexer ist und unterschiedliche Szenarien und Variablen vorliegen, die sich auf die Ergebnisse auswirken können, müssen Sie sie wahrscheinlich nachahmen, um gründlichere Tests durchführen zu können.

2
user82096

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. Zum Beispiel bietet moto Funktionen, die der tatsächlichen S3-API sehr ähnlich sind.

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

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

1
whummer