Testen eines JAX-RS-Webdienstes?

84

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

Grundsätzlich brauche ich eine Möglichkeit, um bestimmte Eingaben zu senden und zu überprüfen, ob ich die erwarteten Antworten erhalte. Ich würde dies lieber über JUnit tun, bin mir aber nicht sicher, wie dies erreicht werden kann.

Mit welchem ​​Ansatz testen Sie Ihre Webdienste?

Update: Wie entzik betonte, kann ich durch Entkoppeln des Webdienstes von der Geschäftslogik die Geschäftslogik einem Unit-Test unterziehen. Ich möchte jedoch auch auf die richtigen HTTP-Statuscodes usw. testen.

Einar
quelle
6
Gute Frage - aber ich würde sagen, wenn Sie über HTTP testen, fällt mir auf, dass dies ein Integrationstest ist.
Tom Duckering
Tom. Du liegst absolut richtig. Wir sollten dafür einen Dummy-HTTP-Emulator / Lightweight-Container injizieren. In node.js macht der Welt-Supertest dies. Sie können express.js emulieren.
Fırat KÜÇÜK

Antworten:

34

Jersey wird mit einer großartigen RESTful-Client-API geliefert, die das Schreiben von Komponententests wirklich einfach macht. Siehe die Unit-Tests in den Beispielen, die mit Jersey geliefert werden. Wir verwenden diesen Ansatz, um die REST-Unterstützung in Apache Camel zu testen. Wenn Sie interessiert sind, finden Sie die Testfälle hier

James Strachan
quelle
6
re: now bad link Sie können die in den Trikots / Beispielen genannten Beispiele finden, die Unit-Tests zeigen, indem Sie im Wesentlichen die Konsumenten von Jersey verwenden, um Webressourcen zu verbrauchen. download.java.net/maven/2/com/sun/jersey/samples/bookstore/…
Rogerdpack
2
Dieses Projekt ist auf GitHub, finden Sie die Tests im Ordner src / test: github.com/jersey/jersey/tree/master/examples/bookstore-webapp
Venkat
2
Ich bezweifle diese Antwort nicht, aber ich finde es unglaublich lustig, dass Jersey immer den Weg in ein JAX-RS-Gespräch findet, wenn es in einigen Fällen (WebSphere leider, um genau zu sein) nicht verfügbar ist und 99% aller akzeptablen Antworten liefert on Stack Overflow null und nichtig.
26

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

Johan
quelle
1
Ich habe Ihren Beitrag abgestimmt, weil die Bibliothek gut aussah, aber sie verwenden sicher viele abhängige Gläser ...
Perry Tew
17

Wie James sagte; Es gibt ein eingebautes Test-Framework für Jersey. Ein einfaches Beispiel für eine Hallo-Welt kann folgendermaßen aussehen:

pom.xml für die Maven-Integration. Wenn du rennst mvn test. Frameworks starten einen Grizzly-Container. Sie können Jetty oder Tomcat verwenden, indem Sie Abhängigkeiten ändern.

...
<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);
    }
}

Sie können diese Beispielanwendung überprüfen .

Fırat KÜÇÜK
quelle
Mit Jersey 2.29.1 musste ich jersey-hk2als Abhängigkeit hinzufügen, da ein java.lang.IllegalStateException: InjectionManagerFactorynicht gefundener Fehler angezeigt wurde (siehe diese Frage ). Ansonsten funktioniert dieses Beispiel gut.
Sarah N
7

Sie haben wahrscheinlich Java-Code geschrieben, der Ihre Geschäftslogik implementiert, und dann den Webdienst-Endpunkt dafür generiert.

Eine wichtige Aufgabe ist es, Ihre Geschäftslogik unabhängig zu testen. Da es sich um reinen Java-Code handelt, können Sie dies mit regelmäßigen JUnit-Tests tun.

Da der Webdienstteil nur ein Endpunkt ist, möchten Sie sicherstellen, dass die generierten Leitungen (Stubs usw.) mit Ihrem Java-Code synchronisiert sind. Sie können dies tun, indem Sie JUnit-Tests schreiben, die die generierten Java-Clients des Webdienstes aufrufen. Auf diese Weise erfahren Sie, wann Sie Ihre Java-Signaturen ändern, ohne die Webdienste zu aktualisieren.

Wenn Ihre Web-Service-Installation bei jedem Build automatisch von Ihrem Build-System generiert wird, müssen die Endpunkte möglicherweise nicht getestet werden (vorausgesetzt, alles wird ordnungsgemäß generiert). Hängt von Ihrer Paranoia ab.

entzik
quelle
2
Sie haben völlig Recht, obwohl ich auch die tatsächlichen HTTP-Antworten testen muss, die zurückgegeben werden, insbesondere die HTTP-Statuscodes.
Einar
6

Obwohl es ab dem Datum der Veröffentlichung der Frage zu spät ist, dachte ich, dass dies für andere nützlich sein könnte, 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 es verwenden, um Ihre Tests auf leichten Containern wie Grizzly, HTTPServer und / oder EmbeddedGlassFish auszuführen. Das Framework kann auch verwendet werden, um Ihre Tests auf einem normalen Webcontainer wie GlassFish oder Tomcat auszuführen.

Bill die Eidechse
quelle
Haben Sie ein gutes Beispiel dafür, wie man Anrufbearbeiter verspottet? JerseyHttpCall -> MyResource -> CallHandler.getSomething () Wie können wir CallHandler hier verspotten?
Balaji Boggaram Ramanarayan
3

Ich verwende Apaches HTTPClient (http://hc.apache.org/) , um Restful Services aufzurufen. Mit der HTTP-Client-Bibliothek können Sie auf einfache Weise Abrufen, Veröffentlichen oder andere von Ihnen benötigte Vorgänge ausführen. Wenn Ihr Dienst JAXB für die XML-Bindung verwendet, können Sie einen JAXBContext erstellen, um die Ein- und Ausgaben der HTTP-Anforderung zu serialisieren und zu deserialisieren.

Chris Dail
quelle
3

Schauen Sie sich den Alchemy Rest Client Generator an . Dies kann eine Proxy-Implementierung für Ihre JAX-RS-Webservice-Klasse mithilfe des Jersey-Clients hinter den Kulissen generieren. Tatsächlich werden Sie Webservice-Methoden als einfache Java-Methoden aus Ihren Unit-Tests bezeichnen. Behandelt auch die http-Authentifizierung.

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

Haftungsausschluss: Ich bin der Autor dieser Bibliothek.

Ashish Shinde
quelle
2

Halte es einfach. Schauen Sie sich https://github.com/valid4j/http-matchers an, die aus 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...
Keyoxy
quelle
1

Eine wichtige Aufgabe ist es, Ihre Geschäftslogik unabhängig zu testen

Ich würde sicherlich nicht davon ausgehen, dass die Person, die den JAX-RS-Code geschrieben hat und die Schnittstelle einem Unit-Test unterziehen möchte, aus irgendeinem bizarren, unerklärlichen Grund die Vorstellung nicht wahrnimmt, dass sie andere Teile des Programms einem Unit-Test unterziehen kann. einschließlich Geschäftslogikklassen. 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 verfügen über Clientanwendungen, und im Fall von RESTEasy können Sie dieselben Anmerkungen verwenden (sogar die mit Anmerkungen versehene Schnittstelle und die Verwendung auf der Client- und Serverseite Ihrer Tests berücksichtigen).

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


quelle
Die Leute möchten vielleicht einige Querschnittsthemen testen. Zum Beispiel Validierung, Authentifizierung, gewünschte HTTP-Header usw. Daher können Benutzer lieber ihren JAX-RS-Code testen.
Fırat KÜÇÜK
In meiner Anwendung verwende ich ModelMapper, um "DTO" -Klassen "Geschäftsobjekt" -Klassen zuzuordnen, die von den zugrunde liegenden "Service" -Klassen verstanden werden. Dies ist ein Beispiel für etwas, das sich gut testen lässt.
Jkerak
Und manchmal ist das REST-Applet so wenig komplex, dass die Mocks größer sind als die Anwendungsschicht, wie in meinem aktuellen Fall. :)
tekHedd
1

Soweit ich weiß, besteht der Hauptzweck des Autors dieses Problems darin, die JAX RS-Schicht von der Business-Schicht zu entkoppeln. Und Unit-Test nur der erste. Zwei grundlegende Probleme müssen wir hier lösen:

  1. Führen Sie im Test einen Web- / Anwendungsserver aus und fügen Sie JAX RS-Komponenten ein. Und nur sie.
  2. Mock Business Services innerhalb der JAX RS-Komponenten / REST-Schicht.

Der erste wird mit Arquillian gelöst. Der zweite ist perfekt in Arquillican und Mock beschrieben

Hier ist ein Beispiel für den Code. Er kann abweichen, wenn Sie einen anderen Anwendungsserver verwenden. Ich hoffe jedoch, dass Sie die Grundidee und die Vorteile erhalten.

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. Hier wird die JAX RS-Konfiguration ohne web.xml verwendet.
  2. Hier wird der JAX RS Client verwendet (kein RESTEasy / Jersey, sie bieten eine bequemere API)
  3. Wenn der Test beginnt, beginnt Arquillians Läufer zu arbeiten. Hier erfahren Sie, wie Sie Tests für Arquillian mit dem erforderlichen Anwendungsserver konfigurieren.
  4. Abhängig vom ausgewählten Anwendungsserver unterscheidet sich eine URL im Test geringfügig. Ein anderer Port kann verwendet werden. 8181 wird in meinem Beispiel von Glassfish Embedded verwendet.

Hoffe, es wird helfen.

Alexandr
quelle
Wird diese Art von Dingen durch das eingebaute Trikot-Test-Framework unterstützt? Ich zögere nur, "noch ein Framework" und all seine Gläser zu meinem Projekt hinzuzufügen, wenn ich bereits etwas mit Jersey zur Verfügung habe.
Jkerak