Interaktionen mit externen Diensten testen

8

Voraussetzungen: Ich verwende die neueste Version von Play! Framework und die Java-Version (nicht Scala).

Ich muss eine Nachricht in einer Nachrichtenwarteschlange veröffentlichen, wenn ein Benutzer erstellt wird, und ich möchte dieses Verhalten testen. Mein Problem besteht darin, dies leicht testbar zu machen.

Der Controller Ansatz

In anderen Frameworks würde ich die Konstruktorinjektion im Controller verwenden und in meinen Tests eine verspottete Warteschlange übergeben. jedoch mit Play! Die Controller sind statisch, dh ich kann in meinen Tests new MyController(mockedQueue) nicht ausführen.

Ich könnte Google Guice verwenden und eine @Inject Annotation in ein statisches Feld in meinem Controller einfügen, aber das fühlt sich für mich nicht sehr nett an, da es bedeutet, dass ich das Feld veröffentlichen muss, um es in der Test, oder ich muss einen Container in meinen Tests verwenden. Ich würde viel lieber die Konstruktorinjektion verwenden, aber Play! scheint das nicht zu erleichtern.

Der Modellansatz

Es wird oft gesagt, dass Ihre Logik in Ihrem Modell sein sollte, nicht in Ihrem Controller. Das macht Sinn; Allerdings sind wir hier nicht in Ruby und die Interaktion Ihrer Entitäten mit externen Diensten (E-Mail, Nachrichtenwarteschlangen usw.) ist wesentlich weniger testbar als in einer dynamischen Umgebung, in der Sie Ihre statischen MessageQueue -Aufrufe einfach durch eine Mocked ersetzen könnten Instanz nach Belieben.

Wenn ich meine Entity in die Warteschlange abruft, wie ist das testbar?

Natürlich sind diese beiden Situationen unnötig, wenn ich End-to-End-Integrationstests durchführe, aber ich brauche lieber keine Nachrichtenwarteschlange oder keinen SMTP-Server, um die Tests zu starten.

Meine Frage ist also: Wie modelliere ich mein Spiel? Controller und / oder Modelle, um das Testen von Interaktionen mit externen Diensten zu erleichtern?

    
James Gregory 18.09.2011, 16:36
quelle

4 Antworten

3

Wie ich sehe, gibt es dafür keine saubere Lösung.

Sie könnten eine abstrakte Fabrik für Ihre Abhängigkeiten verwenden. Diese Fabrik könnte Setter-Methoden für die Objekte haben, die sie erzeugt.

%Vor%

}

Ihr Test würde so aussehen:

%Vor%

Wenn Sie die Setter-Methoden der Factory nicht verfügbar machen möchten, können Sie eine Schnittstelle erstellen und die implementierende Klasse in Ihren Tests konfigurieren.

Eine andere Option wäre die Verwendung von PowerMock zum Verspotten von statischen Methoden. Ich habe es vorher benutzt und es funktioniert in den meisten Fällen relativ gut. Übertreib es einfach nicht, sonst bist du in der Wartungshölle ...

Und schließlich, da Sie bereit sind, Guice in Ihrer Anwendung zu verwenden, dies könnte eine brauchbare Option sein.

Viel Glück!

    
Andre Rodrigues 29.09.2011, 15:02
quelle
2

Ich bin etwas verwirrt. Sie können eine Methode einer anderen Klasse aufrufen

%Vor%

Sie können Komponententestfälle für QueueService mit einem Schein- und Funktions-Testfall für die Benutzer -Controller speichern -Methode schreiben.

    
basav 18.09.2011 19:24
quelle
2

EDIT: Erweiterung der Antwort als vorherige war nicht klar

Die erste Idee wäre, dem Modell den Verweis auf die Warteschlange hinzuzufügen, da Sie ein POJO und Zugriff auf den Konstruktor haben. Wie Sie in den Kommentaren unten erwähnen, ist der Model-Ansatz problematisch, wenn Sie daran denken, dass Hibernate die Entity hydratisiert, was dies verwirft.

Der zweite Ansatz wäre, diese Referenz der Warteschlange zum Controller hinzuzufügen. Nun, das scheint eine schlechte Idee zu sein. Neben der von Ihnen angesprochenen Frage des öffentlichen Mitglieds glaube ich, dass die hinter dem Controller stehende Idee darin besteht, die Parameter der Anfrage abzurufen, deren Richtigkeit zu überprüfen (checkAuthenticity, Validierung usw.), die zu bearbeitende Anfrage zu senden und dann die Antwort vorzubereiten.

Der "Schlüssel" hier ist "sende die Anfrage zur Verarbeitung". In einigen Fällen können wir das im Controller tun, wenn es einfach ist, aber in anderen Fällen scheint es besser, einen "Service" (um es irgendwie zu nennen) zu verwenden, in dem Sie die Arbeit tun, die Sie mit den gegebenen Daten brauchen.

Ich benutze diese Trennung, da es für mich einfacher ist (für mich), den Controller über Selenium zu testen und einen separaten Test (mit JUnit) für den Service durchzuführen.

In diesem Fall würde dieser Service den Verweis auf die von Ihnen erwähnte Warteschlange enthalten.

Bei der Initialisierung hängt das davon ab. Sie können einen Singleton erstellen, ihn jedes Mal über einen Konstruktor initialisieren usw. In Ihrem speziellen Szenario hängt dies möglicherweise von der Arbeit im Zusammenhang mit der Initialisierung Ihres Warteschlangendienstes ab: Wenn es schwierig ist, möchten Sie vielleicht einen Singleton mit einer Factory-Methode, die den Service abruft ( und kann beim Testen verspottet werden) und übergibt dies als Parameter an den Konstruktor des Service-Objekts.

Ich hoffe, dieses Update verdeutlicht mehr, was ich vorhatte, als ich antwortete.

    
Pere Villega 22.09.2011 13:54
quelle
1

Es ist vielleicht nicht das, was Sie suchen, aber in meinem aktuellen Projekt haben wir diese Art von Tests durch Integrationstests und ein JMS-Setup mit einer lokalen Warteschlange und einer Messaging-Bridge gelöst.

Etwas detaillierter:

  • Ihr Code sendet / liest Nachrichten immer in / aus lokalen Warteschlangen, d. h. Warteschlangen auf Ihrem lokalen Anwendungsserver (nicht auf dem externen System).
  • Eine Nachrichtenbrücke verbindet die lokale Warteschlange bei Bedarf mit der Warteschlange des externen Dienstes, z. in der Produktion oder in einer manuellen Testumgebung.
  • Ein Integrationstest erstellt den neuen Benutzer (oder was auch immer Sie testen möchten) und liest dann die erwartete Nachricht aus der lokalen Warteschlange. In diesem Fall ist die Messaging Bridge nicht aktiv.

In meinem Projekt verwenden wir SoapUI , um diese Tests durchzuführen, da das zu testende System eine SOAP-basierte Integrationsplattform ist und SoapUI hat gute JMS-Unterstützung. Es könnte aber auch ein einfacher JUnit-Test sein, der den Test durchführt und danach aus der lokalen JMS-Warteschlange liest.

    
henko 25.09.2011 20:39
quelle