Testen eines REST-Clients gegen einen REST-Server. Wie mache ich Fixtures?

10

Beim Schreiben von Komponententests werden häufig Fixtures verwendet: wenig testbare Daten, daher können wir sagen: 1. Alle Kunden sollten Willy Wonka enthalten. 2. Löschen Sie Client 3, und erhalten Sie jetzt Clients, die Willy Wonka nicht mehr enthalten sollten.

Das ist gut für Unit-Tests. Verwenden Sie Setup / Teardown, um die Fixtures neu zu laden oder die Transaktion zurückzusetzen. Das Testen, Erstellen, Aktualisieren und Löschen erfolgt also innerhalb einer Transaktion . Die neuen temporären Daten dauern nur für die Dauer dieses Tests und werden dann zurückgesetzt.

Aber was ist, wenn wir den REST-Server vom REST-Client getrennt haben?

Wir möchten sicherstellen, dass unser REST-Client nicht nur richtig liest, sondern auch richtig erstellt, aktualisiert und löscht.

Ich konnte keine Beispiele oder Vorschläge finden, wie dies gegen einen Remote-Test-REST-Server durchgeführt werden kann.

Angenommen, ich habe einen Test-REST-Server, der nur Fixtures bedient. Die ganze Staatenlosigkeit von HTTP bedeutet, dass es schwierig ist, eine Nachricht vom Typ "BEGIN TRANSACTION" und "ROLLBACK TRANSACTION" oder "RELOAD FIXTURES" zu senden, oder?

Ich kann nicht der Erste sein, der dies tun möchte, daher habe ich das Gefühl, dass ich eine andere Art brauche, darüber nachzudenken.

Irgendwelche Vorschläge?

Sivers
quelle
Vielleicht können Sie, da es sich um einen Testserver handelt, einen Endpunkt haben, der die Geräte neu lädt?
David Radcliffe
Wenn Ihr Hauptproblem darin besteht, Ihren Testserver wieder in einen vordefinierten Zustand zu versetzen, warum fügen Sie Ihrer Rest-API keine speziellen Testfunktionen wie "RELOAD TESTDATA" hinzu, um das zu tun, was Sie wollen? Natürlich sollten Sie sicherstellen, dass diese Art von API-Aufrufen in der Produktion nicht verfügbar ist.
Doc Brown

Antworten:

7

Softwaresysteme haben idealerweise genau definierte Systemgrenzen und Schnittstellen zwischen ihnen. REST-Services sind hierfür gute Beispiele.

Zu diesem Zweck würde ich empfehlen , gegen das , was Sie zu tun versuchen.

Speziell:

Wir möchten sicherstellen, dass unser REST-Client nicht nur richtig liest, sondern auch richtig erstellt, aktualisiert und löscht.

Was ich stattdessen vorschlagen würde, ist:

  • Erstellen Sie Tests für Ihren REST-Client, um sicherzustellen, dass er sich bei bestimmten Ein- und Ausgaben korrekt verhält. Berücksichtigen Sie gute (erwartete) und schlechte (unerwartete) Werte.

  • Erstellen Sie Tests für Ihren REST-Service (wenn Sie ihn steuern), um sich gemäß der beabsichtigten Funktion zu verhalten

  • Halten Sie Tests in der Nähe ihrer Problemdomäne, damit sie das Design und die Entwicklung dessen unterstützen können, was in diesem Kontext wichtig ist

Briandoll
quelle
3
Sie lehnen die ganze Idee der Integrationstests hier ganz beiläufig ab. Ich denke nicht, dass dieser Ansatz aus der Praxis stammt.
Febeling
Vielen Dank für alle hilfreichen Vorschläge. Auch von Twitter habe ich tolle Vorschläge bekommen, Ruby Gem "Webmock" zu testen und ähnlich die REST API Server Antwort zu verspotten. Ich stimme auch dem "Febeling" zu, dass das, was ich beschreibe, eher ein Integrationstest zu sein scheint, also werde ich das separat untersuchen. Nochmals vielen Dank an alle. - Derek
Sivers
Das Verspotten einer API ist eine großartige Möglichkeit, das Problem zu lösen. Aber wie stellen Sie sicher, dass die verspottete API == echte API ist?
FrEaKmAn
4

Zwei Winkel, die hier zu beachten sind:

  • Testen Sie Ihren Code oder die Installation? Vorausgesetzt, Sie verwenden einen bekannten Service- und Client-Stack, können Sie wahrscheinlich davon ausgehen, dass die Tester und Tausende von Benutzern im Allgemeinen sicherstellen, dass die Grundlagen keinen grundlegenden Fehler enthalten.
  • Warum sind Ihre Tests nicht idempotent? Erstellen Sie eine Möglichkeit, Nichtproduktionsdaten zu schreiben oder auf einen anderen Endpunkt zu schreiben. Wählen Sie ein vorhersehbares Namensmuster. Laden Sie die Rest-Server-Datenbank vor den Tests vor. Und es gibt wahrscheinlich noch ein paar Möglichkeiten, um dies zu erreichen - die Methode ist wirklich taktisch und sollte von der Art der App abhängen.
Wyatt Barnett
quelle
2

Ich denke, das Fälschen der REST-Serverantworten ist der beste Weg, um den Client zu testen.

Für Ruby gibt es das Juwel FakeWeb, mit dem Sie falsche Antworten ausgeben können - https://github.com/chrisk/fakeweb .

In JavaScript können Sie auch so etwas wie Sinon.JS verwenden, wodurch Sie einen gefälschten Server erhalten - http://sinonjs.org/docs/#fakeServer .

laktek
quelle
1

Wie andere bereits gesagt haben, müssen Sie beim Testen eines Clients nicht so weit gehen, auf dem Server zu erstellen, zu löschen usw. In den meisten Fällen müssen Sie sich überhaupt nicht über einen Server lustig machen. Sie müssen wirklich nur sicherstellen, dass Sie die richtigen Anforderungen stellen und die Antworten korrekt verarbeiten. Unabhängig davon, ob sie in Ruby, Python, PHP oder irgendetwas anderem geschrieben sind, wird Ihr Client wahrscheinlich irgendwann eine Methode aus einer HTTP-Bibliothek verwenden, um sie zu erstellen eine Anfrage und es reicht aus, diese Methode zu verspotten, zu überprüfen, wie sie aufgerufen wird, und ein Testergebnis zurückzugeben.

Nehmen Sie einen hypothetischen Python-Client, der urllib2zum Erstellen von Anforderungen verwendet wird. Sie haben wahrscheinlich eine Methode im Client, nennen wir sie get(), die einen Aufruf enthält urllib2.Request(). Sie müssen sich nur wirklich über den Anruf Ihrer eigenen Klasse lustig machen get().

@patch('your.Client.get')
def test_with_mock(self, your_mock):
    your_mock.return_value({'some': 'json'})
    test_obj = your.Client.get_object(5)
    your_mock.assert_called_with('/the/correct/endpoint/5')

In diesem sehr vereinfachten Beispiel wird die Mock-Bibliothek von Python verwendet, um eine hypothetische your.ClientKlasse mit einer get_object()Methode zu testen , die die richtige URL generiert, um etwas von einer API abzurufen. Um die Anfrage zu stellen, ruft der Client seine get()Methode mit dieser URL auf. Hier wird diese Methode verspottet ( your.Client.getwird "gepatcht", so dass sie unter der Kontrolle von steht your_mock), und der Test prüft, ob der richtige Endpunkt angefordert wurde.

Die verspottete Methode gibt die konfigurierte JSON-Antwort ( your_mock.return_value) zurück, die der Client verarbeiten muss, und Sie würden weitere Aussagen treffen, um zu testen, ob die erwarteten Daten auf die erwartete Weise verarbeitet wurden.

Sean Redmond
quelle
Aber wie sind Sie sicher, dass es sich bei der "richtigen" Anfrage um eine tatsächliche richtige (in der Produktion) Anfrage handelt? Denn wenn ich Ihren Vorschlag verstehe, wenn sich die API ändert oder bricht, funktionieren Ihre Tests immer noch. Während der Produktion ist es eine ganz andere Geschichte.
FrEaKmAn
1

Was Sie beschreiben, ist ein Integrationstestszenario. Diese sind normalerweise etwas umständlich einzurichten und abzureißen. Es macht sie langsam zu laufen und ziemlich oft spröde.

Der Ansatz mit Fixtures ist genauso umständlich und ungeschickt, aber es ist die Standardmethode, mit der einige Frameworks vorgehen, z. B. Rails, und er wird bereits unterstützt. Sie benötigen den abstrakten Testfall oder ähnliches, um die Datenbank mit Fixtures vorzubereiten. (Vorsicht vor ungewöhnlichen Benennungen von Testkategorien in Rails, die Unit-Tests mit DB-Fixtures sind streng genommen auch Integrationstests.)

Ich würde in Ihrem Szenario akzeptieren, dass Sie eine testspezifische Kontrolle über den API-Anwendungsstatus oder die Datenbank haben. Sie können entweder zusätzliche Endpunkte für das Einrichten und Herunterfahren haben, die nur in der Testumgebung vorhanden sind. Alternativ können Sie mit der Datenbank (oder was auch immer Sie verwenden) hinter der Rückseite Ihrer Anwendung / API sprechen.

Wenn Sie der Meinung sind, dass dies zu viel (im Sinne eines unangemessenen) Aufwands ist, sollten Sie berücksichtigen, dass der Ansatz mit Fixtures für Datenbanken genau dies bewirkt: Verwenden zusätzlicher, testspezifischer Mittel zum Manipulieren des Datenbank- oder Anwendungsstatus.

Ich glaube nicht, dass diese Diskussion mit der Staatenlosigkeit von HTTP zu tun hat. HTTP ist zustandslos, die Anwendung jedoch in den meisten Fällen definitiv nicht. Es klingt ein bisschen so, als ob Sie nach REST-Strenge suchen. Genauso gut können alle Ressourcen vollständig erstellt, gelesen und gelöscht werden. In diesem Fall können Sie alle Einstellungen und Abbrüche über reguläre API-Mittel vornehmen. In der Praxis wird dies jedoch häufig nicht durchgeführt, da Sie bestimmte Vorgänge aus einem Geschäftsverständnis Ihrer Anwendung nicht einbeziehen möchten, zumindest nicht außerhalb der Testumgebung.

Febeling
quelle
1

Affen Patch

Bei meiner Arbeit machen wir ATDD, indem wir ein bestehendes xUnit-Framework verwenden und Netzwerkaufrufe zwischen Client und Server patchen. Im selben Prozessbereich laden wir den Client und patchen den Netzwerkaufruf am Anfang des REST-Server-Stack-Codes. Alle Anrufe werden dann vom Client wie gewohnt ausgegeben, und der Servercode erhält die Anforderungen genau so, wie sie normalerweise angezeigt werden.

Leistungen

  • Keine Synchronisierung mit dem Serverstart (da kein Server vorhanden ist)
  • Nutzen Sie die klassische Methode zum Einrichten und Herunterfahren von Einheiten, um beispielsweise Vorrichtungen zu verwalten
  • Fähigkeit, Mocks / Stubs und andere Affenpflaster für eine feinkörnigere Kontrolle des Tests zu verwenden
  • kann mit einem xUnit-Rahmen geschrieben werden

Kompromisse

  • legt keine Interaktionen / Probleme mit mehreren Prozessen offen (Sperren, Ressourcenmangel usw.)
  • legt keine Probleme mit mehreren Servern offen (Datenserialisierung, Clustering-Stil)
  • legt keine Netzwerkprobleme offen, da dies simuliert wird (Zugriff, Timeout-Fehler usw.)
Dietbuddha
quelle