2008-09-23 11 views
71

Ich suche derzeit nach Möglichkeiten, um automatisierte Tests für einen JAX-RS (Java API für RESTful Web Services) basierten Web Service zu erstellen.Unit testet einen JAX-RS Web Service?

Ich brauche grundsätzlich einen Weg, um bestimmte Eingaben zu senden und zu überprüfen, dass ich die erwarteten Antworten bekomme. Ich würde es vorziehen, dies über JUnit zu tun, aber ich bin nicht sicher, wie das erreicht werden kann.

Welchen Ansatz verwenden Sie, um Ihre Web-Services zu testen?

Update: Wie entzik darauf hingewiesen, entkoppeln die Web-Service von der Geschäftslogik ermöglicht es mir Unit-Test die Geschäftslogik. Ich möchte aber auch nach den richtigen HTTP-Statuscodes usw. suchen.

+5

Gute Frage - aber ich würde sagen, dass wenn Sie über HTTP zu testen sind dann es mir auffällt, dass diese Integrationstests ist. –

+0

Tom. Du liegst absolut richtig. Wir sollten dafür einen Dummy-HTTP-Emulator/Lightweight-Container einfügen. In node.js macht World Supertest dies. Sie können express.js emulieren. –

Antwort

35

Jersey kommt mit einer großen RESTful-Client-API, die das Schreiben von Komponententests wirklich einfach macht. Siehe die Komponententests in den Beispielen, die mit Jersey geliefert werden. Wir verwenden diesen Ansatz die REST-Unterstützung in Apache Camel zu testen, ob Sie die test cases are here

+6

re: now bad link Sie können die Beispiele in den Trikots/Stichproben, die Unit-Tests zeigen, im Grunde durch die Verwendung von Trikots Verbraucher Web-Ressourcen zu verbrauchen. http://download.java.net/maven/2/com/sun/jersey/samples/bookstore/1.1.5-ea-SNAPSHOT – rogerdpack

+3

wieder schlechte Verbindung - ein Update beachten? –

+2

Dieses Projekt ist auf GitHub, finden Sie die Tests in der src/test-Ordner: https://github.com/jersey/jersey/tree/master/examples/bookstore-webapp – Venkat

5

Sie haben wahrscheinlich einen Java-Code geschrieben, der Ihre Geschäftslogik implementiert, und dann haben Sie den Endpunkt der Webdienste generiert.

Eine wichtige Sache zu tun ist, Ihre Geschäftslogik unabhängig zu prüfen. Da es sich um reinen Java-Code handelt, können Sie dies mit regulären JUnit-Tests tun.

Jetzt, da der Web-Service-Teil nur ein Endpunkt ist, was Sie sicherstellen möchten, ist, dass die generierten Rohrleitungen (Stubs, etc.) mit Ihrem Java-Code synchronisiert sind. Sie können dies tun, indem Sie JUnit-Tests schreiben, die die generierten Web-Service-Java-Clients aufrufen. Dadurch werden Sie wissen, wenn Sie Ihre Java-Signaturen ändern, ohne die Web-Services zu aktualisieren.

Wenn Ihre Web-Services-Installation automatisch von Ihrem Build-System bei jedem Build generiert wird, ist es möglicherweise nicht erforderlich, die Endpunkte zu testen (vorausgesetzt, es ist alles korrekt generiert). Hängt von deiner Paranoia ab.

+1

Sie haben recht, obwohl ich auch die tatsächlichen HTTP-Antworten testen muss, die zurückgegeben werden, insbesondere die HTTP-Statuscodes. – Einar

3

Ich verwende Apache HTTPClient (http://hc.apache.org/), um Restful Services anzurufen. Mit der HTTP-Client-Bibliothek können Sie einfach get, post oder andere Operationen ausführen, die Sie benötigen. Wenn Ihr Dienst JAXB für die XML-Bindung verwendet, können Sie einen JAXBContext zum Serialisieren und Deserialisieren von Ein- und Ausgaben aus der HTTP-Anforderung erstellen.

6

Obwohl es zu spät ist ab dem Zeitpunkt der Veröffentlichung der Frage interessiert sind, dachte, dies für andere nützlich sein könnten, die eine ähnliche Frage haben. Jersey wird mit einem Testframework namens Jersey Test Framework geliefert, mit dem Sie Ihren RESTful-Webdienst einschließlich der Antwortstatuscodes testen können. Sie können damit Tests auf Lightweight-Containern wie Grizzly, HTTPServer und/oder EmbeddedGlassFish ausführen. Das Framework kann auch dazu verwendet werden, Ihre Tests auf einem normalen Webcontainer wie GlassFish oder Tomcat auszuführen.

+0

Haben Sie ein gutes Beispiel dafür, wie Sie Call-Handler nachahmen können? JerseyHttpCall -> MyResource -> CallHandler.getSomething() Wie können wir CallHandler hier verspotten? –

2

Eine wichtige Sache zu tun ist, unabhängig Logik Ihres Unternehmens

Ich würde nicht davon ausgehen, dass die Person, die den JAX-RS-Code geschrieben und sucht, um Unit-Test zu testen, die Interface ist irgendwie, für irgendeinen bizarren, unerklärlichen Grund, ohne die Vorstellung, dass er oder sie Unit-Tests anderer Teile des Programms, einschließlich Business-Logik-Klassen, testen kann. Es ist kaum hilfreich, das Offensichtliche zu sagen, und es wurde wiederholt darauf hingewiesen, dass auch die Antworten getestet werden müssen.

Sowohl Jersey als auch RESTEasy haben Client-Anwendungen, und im Fall von RESTEasy können Sie die gleichen Annotationen verwenden (Annotationsschnittstelle sogar ausklammern und auf der Client- und Serverseite Ihrer Tests verwenden).

REST nicht, was dieser Service für Sie tun kann; REST, was Sie für diesen Service tun können.

+0

Die Leute möchten einige cross-cutting Bedenken testen. Zum Beispiel Validierung, Authentifizierung, gewünschte HTTP-Header usw. So können Leute ihren JAX-RS-Code testen. –

+0

In meiner Anwendung verwende ich ModelMapper, um von "DTO" -Klassen zu "Business-Objekt" -Klassen "zuordnen", die von den zugrunde liegenden "Service" -Klassen verstanden werden. Dies ist ein Beispiel für etwas, das unabhängig getestet werden könnte. – jkerak

22

Sie können REST Assured ausprobieren, was es sehr einfach macht, REST-Dienste zu testen und die Antwort in Java zu validieren (mit JUnit oder TestNG).

+1

Ich habe deinen Beitrag gewählt, weil die Bibliothek gut aussieht, aber sie benutzen sicher viele abhängige Gläser ... –

10

Wie James sagte; Es ist eingebaut test framework für Jersey. Ein einfaches Hello-World-Beispiel kann folgendermaßen aussehen:

pom.xml für Maven-Integration. Wenn Sie mvn test ausführen. Frameworks starten einen Grizzly-Container. Sie können Anlegesteg oder Kater über sich ändernde Abhängigkeiten verwenden.

... 
<dependencies> 
    <dependency> 
    <groupId>org.glassfish.jersey.containers</groupId> 
    <artifactId>jersey-container-servlet</artifactId> 
    <version>2.16</version> 
    </dependency> 

    <dependency> 
    <groupId>org.glassfish.jersey.test-framework</groupId> 
    <artifactId>jersey-test-framework-core</artifactId> 
    <version>2.16</version> 
    <scope>test</scope> 
    </dependency> 

    <dependency> 
    <groupId>org.glassfish.jersey.test-framework.providers</groupId> 
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId> 
    <version>2.16</version> 
    <scope>test</scope> 
    </dependency> 
</dependencies> 
... 

ExampleApp.java

import javax.ws.rs.ApplicationPath; 
import javax.ws.rs.core.Application; 

@ApplicationPath("/") 
public class ExampleApp extends Application { 

} 

HelloWorld.java

import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 
import javax.ws.rs.core.MediaType; 

@Path("/") 
public final class HelloWorld { 

    @GET 
    @Path("/hello") 
    @Produces(MediaType.TEXT_PLAIN) 
    public String sayHelloWorld() { 

     return "Hello World!"; 
    } 
} 

HelloWorldTest.java

import org.glassfish.jersey.server.ResourceConfig; 
import org.glassfish.jersey.test.JerseyTest; 
import org.junit.Test; 
import javax.ws.rs.core.Application; 
import static org.junit.Assert.assertEquals; 

public class HelloWorldTest extends JerseyTest { 

    @Test 
    public void testSayHello() { 

     final String hello = target("hello").request().get(String.class); 

     assertEquals("Hello World!", hello); 
    } 

    @Override 
    protected Application configure() { 

     return new ResourceConfig(HelloWorld.class); 
    } 
} 

können Sie prüfen, this Beispielanwendung.

3

Werfen Sie einen Blick auf Alchemy rest client generator. Dies kann eine Proxy-Implementierung für Ihre JAX-RS-Webservice-Klasse mit dem Trikot-Client hinter der Szene generieren. Effektiv werden Sie Webservice-Methoden als einfache Java-Methoden aus Ihren Unit-Tests nennen. Handles HTTP-Authentifizierung auch.

Es ist keine Codegenerierung erforderlich, wenn Sie Tests einfach ausführen müssen, damit es bequem ist.

Disclaimer: Ich bin der Autor dieser Bibliothek.

1

Wie ich verstehe, ist der Hauptzweck des Authere dieses Problems, JAX RS Schicht vom Geschäft zu entkoppeln. Und Unit-Test nur die erste. Zwei grundlegende Probleme hier müssen wir lösen:

  1. Run in Test einige Web/Application Server, setzen JAX RS Komponenten in es. Und nur sie.
  2. Mock Business Services in JAX RS Komponenten/REST-Schicht.

Die erste ist mit Arquillian gelöst. Die zweite ist perfekt beschrieben in arquillican and mock

Hier ist ein Beispiel für den Code, kann es abweichen, wenn Sie einen anderen Anwendungsserver verwenden, aber ich hoffe, Sie erhalten die Grundidee und Vorteile.

import javax.inject.Inject; 
import javax.ws.rs.GET; 
import javax.ws.rs.Path; 

import com.brandmaker.skinning.service.SomeBean; 

/** 
* Created by alexandr on 31.07.15. 
*/ 
@Path("/entities") 
public class RestBean 
{ 
    @Inject 
    SomeBean bean; 

    @GET 
    public String getEntiry() 
    { 
     return bean.methodToBeMoked(); 
    } 
} 

import java.util.Set; 

import javax.ws.rs.ApplicationPath; 
import javax.ws.rs.core.Application; 

import com.google.common.collect.Sets; 

/** 
*/ 
@ApplicationPath("res") 
public class JAXRSConfiguration extends Application 
{ 
    @Override 
    public Set<Class<?>> getClasses() 
    { 
     return Sets.newHashSet(RestBean.class); 
    } 
} 


public class SomeBean 
{ 
    public String methodToBeMoked() 
    { 
     return "Original"; 
    } 
} 

import javax.enterprise.inject.Specializes; 

import com.brandmaker.skinning.service.SomeBean; 

/** 
*/ 
@Specializes 
public class SomeBeanMock extends SomeBean 
{ 
    @Override 
    public String methodToBeMoked() 
    { 
     return "Mocked"; 
    } 
} 

@RunWith(Arquillian.class) 
public class RestBeanTest 
{ 
    @Deployment 
    public static WebArchive createDeployment() { 
     WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war") 
       .addClasses(JAXRSConfiguration.class, RestBean.class, SomeBean.class, SomeBeanMock.class) 
       .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); 
     System.out.println(war.toString(true)); 
     return war; 
    } 

    @Test 
    public void should_create_greeting() { 
     Client client = ClientBuilder.newClient(); 
     WebTarget target = client.target("http://127.0.0.1:8181/test/res/entities"); 
     //Building the request i.e a GET request to the RESTful Webservice defined 
     //by the URI in the WebTarget instance. 
     Invocation invocation = target.request().buildGet(); 
     //Invoking the request to the RESTful API and capturing the Response. 
     Response response = invocation.invoke(); 
     //As we know that this RESTful Webserivce returns the XML data which can be unmarshalled 
     //into the instance of Books by using JAXB. 
     Assert.assertEquals("Mocked", response.readEntity(String.class)); 
    } 
} 

Ein paar Anmerkungen:

  1. JAX RS-Konfiguration ohne web.xml wird hier verwendet.
  2. JAX RS Client wird hier verwendet (kein RESTEasy/Jersey, sie setzen eine bequemere API frei)
  3. Wenn der Test beginnt, beginnt der Läufer von Arquillian zu arbeiten. Here können Sie Tests für Arquillian mit dem benötigten Anwendungsserver konfigurieren.
  4. Je nach gewähltem Anwendungsserver wird eine URL im Test ein wenig abweichen. Ein anderer Port kann verwendet werden. 8181 ist von Glassfish Embedded in meinem Beispiel verwendet.

Hoffe, es wird helfen.

+0

wird diese Art von Sache vom eingebauten Trikot-Test-Framework unterstützt? Ich zögere nur, "noch einen anderen Rahmen" und all seine Gläser zu meinem Projekt hinzuzufügen, wenn ich bereits etwas mit Jersey zur Verfügung habe. – jkerak

2

Halten Sie es einfach. Werfen Sie einen Blick auf https://github.com/valid4j/http-matchers, die von Maven Central importiert werden können.

<dependency> 
     <groupId>org.valid4j</groupId> 
     <artifactId>http-matchers</artifactId> 
     <version>1.0</version> 
    </dependency> 

Anwendungsbeispiel:

// Statically import the library entry point: 
import static org.valid4j.matchers.http.HttpResponseMatchers.*; 

// Invoke your web service using plain JAX-RS. E.g: 
Client client = ClientBuilder.newClient(); 
Response response = client.target("http://example.org/hello").request("text/plain").get(); 

// Verify the response 
assertThat(response, hasStatus(Status.OK)); 
assertThat(response, hasHeader("Content-Encoding", equalTo("gzip"))); 
assertThat(response, hasEntity(equalTo("content"))); 
// etc...