web-dev-qa-db-de.com

Wie rufe ich einen Unit-Test-Code auf, der die Jersey Client-API aufruft?

Ich habe Code geschrieben, der die Jersey-Client-API aufruft, die wiederum einen Webdienst aufruft, auf den ich keinen Einfluss habe. Ich möchte nicht, dass mein Unit-Test den eigentlichen Webdienst aufruft.

Was ist der beste Ansatz, um einen Komponententest für Code zu schreiben, der die Jersey-Client-API aufruft? Sollte ich die Jersey-Server-API verwenden, um einen JAX-RS-Webdienst zu schreiben, und dann das Jersey Test Framework für den Komponententest verwenden? Oder sollte ich die Aufrufe des Jersey-Webservices verspotten? Ich habe Zugriff auf JMock. Oder sollte ich einen anderen Ansatz versuchen?

Während meiner Recherche habe ich diese Diskussion verschiedene Optionen beschrieben, aber ich habe eine vollständige Lösung gefunden. Gibt es Codebeispiele für einen vorgeschlagenen JUnit-Ansatz? Ich konnte keine in der Jersey-Dokumentation finden.

Hier ist der relevante Quellcode:

public String getResult(URI uri) throws Exception {
  // error handling code removed for clarity
  ClientConfig clientConfig = new DefaultClientConfig();
  Client client = Client.create(clientConfig);
  WebResource service = client.resource(uri);
  String result = service.accept(accept).get(String.class);
  return result;
}

Hier sind Beispiele für Testcode, den ich weitergeben möchte. Ich möchte testen, ob (1) eine gültige URI übergeben und eine gültige Zeichenfolge zurückerhalten wird und (2) eine ungültige (aus irgendeinem Grund nicht erreichbare oder nicht autorisierte) URI übergeben und eine Ausnahme zurückerhalten wird.

@Test
public void testGetResult_ValidUri() throws Exception {
  String xml = retriever.getResult(VALID_URI);
  Assert.assertFalse(StringUtils.isBlank(xml));
}

@Test(expected = IllegalArgumentException.class)
public void testGetResult_InvalidUri() throws Exception {
  retriever.getResult(INVALID_URI);
}

Alles oben ist die einfache Beschreibung dessen, was mein Code tut. In Wirklichkeit gibt es darüber eine Ebene, die zwei URIs akzeptiert, zuerst versucht, den ersten URI aufzurufen, und wenn dieser URI fehlschlägt, dann versucht er, den zweiten URI aufzurufen. Ich hätte gerne Unit-Tests, die abdecken, (1) ob der erste URI erfolgreich ist, (2) ob der erste URI fehlschlägt und der zweite URI erfolgreich ist und (3) ob beide URIs fehlschlagen. Dieser Code ist so komplex, dass ich diese verschiedenen Szenarien mit JUnit testen möchte. Dazu muss ich jedoch entweder die eigentlichen Stand-In-Webdienste ausführen oder die Jersey-Client-API-Aufrufe verspotten.

26
BennyMcBenBen

Versuchen Sie, Mockito oder Easymock für verspottete Serviceanrufe zu verwenden. Sie müssen nur die tatsächlich verwendeten Methoden verspotten - und nicht jede Methode. Sie können ein Scheinobjekt für die WebResource-Klasse erstellen und anschließend den Methodenaufruf als Schein übernehmen.

In @ BeforeClass/@ Before JUnit Testmethode schreibe etwas (Mockito-Beispiel)

WebResource res = mock(WebResource.class);
when(res.accept(something)).thenReturn(thatWhatYouWant);

Dann können Sie in Ihren Tests das res-Objekt wie ein reales Objekt verwenden und eine Scheinmethode aufrufen. Anstatt den Wert zurückzugeben, können Sie auch Ausnahmen auslösen. Mockito ist ziemlich cool.

13

Normalerweise ist es das, wonach Sie wirklich streben "Die Art und Weise, wie ich den Jersey Client DSL verwende, erzeugt eine Anfrage an die richtige URL mit den korrekten Payload- und URL-Parametern". Das Testen mit Mockito ist wirklich sehr ausführlich und der Setup-Code sieht normalerweise so aus:

    when(authentication.queryParam(eq("sa"), anyBoolean())).thenReturn(testAuthentication);
    when(testAuthentication.resolveTemplate("channel", "smf")).thenReturn(testAuthentication);
    when(testAuthentication.request(
            MediaType.APPLICATION_JSON_TYPE)).thenReturn(mockRequestBuilder);
    when(mockRequestBuilder.post(any(Entity.class))).thenReturn(mockResponse);
    when(mockResponse.readEntity(ResponseWrapper.class)).thenReturn(successfulAuthResponse());

Und dies ist im Grunde nur für eine einzelne REST -Anforderung. Es ist übertrieben wortreich, und anstatt das erhoffte Ergebnis zu testen, replizieren Sie nur die Schritte, die Sie für richtig halten, wenn Sie den Jersey Client DSL verwenden.

Anstelle des Vorstehenden möchte ich einen einfachen Service verspotten. Hierfür habe ich WireMock verwendet, das einen Jetty-Server startet und wo ich Dinge wie "Erwarte eine Anfrage an diese URL, antworte mit dieser Nachricht und vergewissere dich, dass die Nutzlast dies ist".

Ich weiß, dass dies bei einem Integrationstest zugenommen hat und etwas langsamer ist als nur mit Mockito, aber ich schätze das Testen des tatsächlichen Ergebnisses und ich schätze die Lesbarkeit der Tests in diesem Fall mehr.

Das Setup für einen WireMock-basierten Jersey Client-Test sieht in etwa so aus:

@Test
public void exactUrlOnly() {
    stubFor(get(urlEqualTo("/some/thing"))
            .willReturn(aResponse()
                .withHeader("Content-Type", "text/plain")
                .withBody("Hello world!")));

   assertThat(testClient.get("/some/thing").statusCode(), is(200));
   assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
13
vertti

Implementieren Sie einfach einen ähnlichen Dienst, und starten Sie den Dienst in Ihrem Gerätetest-Setup mithilfe von HttpServerFactory.

0
Chase