Wie kann ich eine Klasse testen, für die ein Webdienstaufruf erforderlich ist?

21

Ich versuche, eine Klasse zu testen, die einige Hadoop-Webdienste aufruft. Der Code ist so ziemlich in der Form:

method() {
    ...use Jersey client to create WebResource...
    ...make request...
    ...do something with response...
}

zB gibt es eine Methode zum Erstellen eines Verzeichnisses, eine Methode zum Erstellen eines Ordners usw.

Wie kann ich dies testen, da der Code einen externen Webdienst betrifft, über den ich keine Kontrolle habe? Ich könnte versuchen, den Webservice-Client / die Antworten zu verspotten, aber das verstößt gegen die Richtlinie, die ich in letzter Zeit häufig gesehen habe: "Verspotten Sie keine Objekte, die Sie nicht besitzen". Ich könnte eine Dummy-Web-Service-Implementierung einrichten - wäre das noch ein "Unit-Test" oder wäre es dann ein Integrationstest? Ist es einfach nicht möglich, einen Unit-Test auf diesem niedrigen Niveau durchzuführen - wie würde ein TDD-Praktiker damit umgehen?

Chris Cooper
quelle
5
Wo haben Sie Anleitungen gesehen, Dinge, die Sie nicht besitzen, nicht zu verspotten? Das scheint ein großer Grund zu sein, warum Sie sich über Dinge lustig machen sollten ...
Thomas Owens
1
Ich habe es in vielen Blogs gelesen, von denen einer auf amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/… verweist, von dem ich weiß, dass es ein angesehenes Buch ist ( blog.8thlight). de / eric-smith / 2011/10/27 / thats-not-yours.html , mockobjects.com/2007/04/test-smell-everything-is-mocked.html )
Chris Cooper
1
@ ChrisCooper: Darf ich darauf hinweisen, dass der letzte Link sehr veraltet ist (ab 2007). Vieles hat sich seitdem geändert. Ich habe das Gefühl, dass das Verspotten damals viel schwieriger war, als man tatsächlich Verhalten implementieren musste,
anstatt

Antworten:

41

Meiner Meinung nach sollten Sie die Webservice-Aufrufe verspotten, wenn dies ein Unit-Test im Gegensatz zu einem Integrationstest ist.

Ihr Komponententest sollte nicht prüfen, ob der externe Webservice funktioniert oder ob Ihre Integration in ihn korrekt ist. Beachten Sie, dass ein Nebeneffekt der Umwandlung Ihres Komponententests in einen Integrationstest darin besteht, dass er wahrscheinlich langsamer abläuft und Sie schnelle Komponententests wünschen, ohne zu dogmatisch zu werden.

Sollte dies auch dazu führen, dass der Unit-Test fehlschlägt, wenn der Webservice vorübergehend nicht verfügbar ist oder nicht ordnungsgemäß funktioniert? Es scheint nicht richtig. Ihr Unit-Test sollte nur aus einem Grund fehlschlagen: wenn der Code in dieser "Unit" einen Fehler enthält.

Der einzige Teil des Codes, der hier relevant ist, ist ...do something with response.... Verspotte den Rest.

Andres F.
quelle
2
Denken Sie daran, dass Sie Ihre Scheinobjektsignatur und die Rückgabewerte während der unvermeidlichen Änderungen an diesem Dienst mit denen synchronisieren müssen, die vom Hadoop-Webservice generiert wurden.
pcurry
Es lohnt sich selten, Standardkomponenten wie Hadoop zu testen. Wenn Sie jedoch einen benutzerdefinierten Webdienst aufrufen, der von einem anderen Team oder einer anderen Organisation bereitgestellt wird, möchten Sie möglicherweise einen Test zur Selbstverteidigung schreiben. Auf diese Weise können Sie im Fehlerfall schnell überprüfen, ob es sich bei dem Problem um Ihren Code oder den Webdienst handelt. Dies ist kein Komponententest, der automatisch ausgeführt wird. Es ist eine Diagnose, die nach Bedarf ausgeführt werden kann.
Kevin Cline
@kevincline Ich stimme voll und ganz der Notwendigkeit der von Ihnen vorgeschlagenen Tests zu, und in der Tat schreibe ich sie in meiner täglichen Arbeit und habe mich als nützlich erwiesen. Aber es handelt sich per Definition NICHT um Unit-Tests, worum es in der Frage ging :) Bedenken Sie Folgendes: Wenn es sich um einen Unit-Test handelt und der Code fehlschlägt, weil der Webservice geändert wurde, was ist die "Unit", die Sie testen? Was genau ist gescheitert? Sie testen nicht isoliert, wie dies beim Komponententest erforderlich ist.
Andres F.
1
@AndresF .: Ich denke, wir sind uns einig: "Diese [Diagnose] ist KEIN
Komponententest
@ Kevincline Richtig! Ich habe Ihren Kommentar falsch verstanden, sorry!
Andres F.
5

Ich bin nicht einverstanden mit "Verspotten Sie keine Objekte, die Sie nicht besitzen", wenn Sie Unit-Tests durchführen.

Mocks Existenzzweck ist die Tatsache, dass es Module, Bibliotheken und Klassen geben wird, die wir nicht besitzen werden.

Mein Vorschlag für Ihr Szenario ist, den Web-Service-Aufruf zu verspotten.

Richten Sie das Mock so ein, dass es Daten an Ihr Modul zurückgibt.
Stellen Sie sicher, dass Sie alle Szenarien abdecken, z. B. wenn die zurückgegebenen Daten null sind, wenn die zurückgegebenen Daten gültig sind usw.

Und für den Code, den Sie besitzen, müssen Sie als Entwickler sicherstellen, dass der von Ihnen erstellte Code in allen Szenarien die erwartete Leistung erbringt.

Venu b
quelle
1

Ich würde für diesen Test so etwas wie EasyMock verwenden. Mocking-Frameworks sind eine ideale Möglichkeit, externe Abhängigkeiten von einer Klasse zu entfernen, und geben Ihnen vollständige Kontrolle über das Ergebnis externer Abhängigkeiten während der Tests. Um Ihr Beispiel ein wenig zu erweitern:

class WebClass {

private WebServiceInterface webserviceInterface;

    void method(){
        R result = webServiceInterface.performWebServiceCall();
        ... do something with result
    }

    public void setWebServiceInterface(WebServiceInterface webServiceInterface){
        this.webServiceInterface = webServiceInterface;
    }
}


interface WebServiceInterface {

   R performWebServiceCall();

}


class WebClassTest {

private WebServiceInterface mock;    
private R sampleResult = new R();

    @Before
    public void before(){
        mock = EasyMock.createMock(WebServiceInterface.class);
    }


    @Test
    public void test() {
        WebClass classUnderTest = new WebClass();
        EasyMock.expect(mock.performWebServiceCall()).andReturn(sampleResult);
        classUnderTest.setWebServiceInterface(mock);
        classUnderTest.method();
        EasyMock.verify(mock);
    }
}

Als Erstes müssen Sie die Logik in Ihrer Klasse extrahieren, in der Sie Jersey verwenden, um eine WebResource abzurufen und den Webdienst in einer separaten Klasse aufzurufen. Wenn Sie ein Interface für diese Klasse erstellen, können Sie einen Mock erstellen, dem Sie dann das Verhalten diktieren können.

Sobald diese Schnittstelle erstellt ist, können Sie mit EasyMock einen Mock erstellen, der ein bestimmtes Objekt gemäß Ihrem Testfall zurückgibt. Das obige Beispiel ist eine Vereinfachung der Strukturierung eines einfachen verspotteten Tests und der Funktionsweise Ihrer Benutzeroberfläche.

Weitere Informationen zum Verspotten von Frameworks finden Sie in dieser Frage . In diesem Beispiel wird auch die Verwendung von Java vorausgesetzt, aber die Spott-Frameworks sind in allen Sprachen verfügbar und obwohl sie unterschiedlich implementiert sind, funktionieren sie im Allgemeinen auf die gleiche Weise

Richard
quelle
1

Mocks sind in diesem Fall akzeptabel, aber Sie brauchen es nicht. Anstelle von Unit-Tests method()sollten Sie stattdessen nur den Teil testen, der die Antwort verarbeitet.

Extrahieren Sie eine Funktion ResponseData(welche Art auch immer geeignet ist) und führen Sie dann die Aktion aus.

Anstatt zu verspotten, erstellen Sie jetzt einfach ein ResponseData-Objekt und übergeben es.

Sie können den Aufruf des Dienstes den vollständigen Integrationstests überlassen - dies wird method()insgesamt abgedeckt

Daenyth
quelle
0

Was ich getan habe und es funktioniert:

  1. Lassen Sie alle Code-Call-Webservices über einen Proxy ausführen.
  2. Der Proxy ruft eine Klasse auf, die statisch weiß, ob wir Proxy verwenden oder nicht, und leitet entsprechend um. Mocks sind nur HashMaps, die für jede Anforderung eine bestimmte Antwort zurückgeben.
  3. Führen Sie die Tests mehrmals in dieser Reihenfolge durch:

3.1 Zunächst werden alle Webservices getestet. Von jeder Maschine aus, auch von Entwicklermaschinen. Dies sind die eigentlichen Webservices, die jedoch in der Entwicklungsumgebung ausgeführt werden. Dies bedeutet, dass Webservices niemals inaktiv sein oder fehlerhafte Werte beantworten können, da sich ansonsten jeder Entwickler beschwert, dass er nicht kompilieren kann.

3.2 Anschließend werden alle applikationsinternen Komponententests durchgeführt. Dies bedeutet, dass alle Webservices mit den gleichen Tests wie in 3.1 verspottet und getestet werden (sie sollten auch bestanden werden, da sonst die Verspottungen falsch sind) und von der realen Anwendung aufgerufen werden, als ob sie tatsächlich verwendet würden. Wenn die Mocks falsch sind, können Sie den Test in 3.1 ausführen und diese (Anforderungs-, Antwort-) Werte in einer HashMap aufzeichnen.

3.3 Dann werden die gleichen Tests wie in 3.2 ausgeführt, diesmal jedoch gegen die echten Webservices, die in der Entwicklungsumgebung ausgeführt werden.

Nachdem all dies abgeschlossen ist, müssen Sie für die reale Produktionsumgebung nur die reale Adresse für jeden Webservice angeben. Hoffentlich erfordert dies nicht zu viele Änderungen in der Konfiguration.

Guillermo Schwarz
quelle