web-dev-qa-db-fra.com

Unité testant un service Web JAX-RS?

Je recherche actuellement des moyens de créer des tests automatisés pour un service Web basé sur JAX-RS (API Java pour les services Web RESTful).

J'ai essentiellement besoin d'un moyen de lui envoyer certaines entrées et de vérifier que j'obtiens les réponses attendues. Je préfère le faire via JUnit, mais je ne sais pas comment cela peut être réalisé.

Quelle approche utilisez-vous pour tester vos services Web?

Mise à jour: Comme l'a souligné entzik, le découplage du service Web de la logique métier me permet de tester de manière unitaire la logique métier. Cependant, je veux également tester les bons codes d'état HTTP, etc.

78
Einar

Jersey est livré avec une excellente API client RESTful qui rend l'écriture de tests unitaires très facile. Voir les tests unitaires dans les exemples livrés avec Jersey. Nous utilisons cette approche pour tester le support REST dans Apache Camel , si vous êtes intéressé par le les cas de test sont ici

35
James Strachan

Vous pouvez essayer REST Assuré ce qui rend très simple à tester REST services et valider la réponse en Java (en utilisant JUnit ou TestNG).

24
Johan

Comme l'a dit James; Il existe un framework de test intégré pour Jersey. Un exemple simple de bonjour peut ressembler à ceci:

pom.xml pour l'intégration de maven. Lorsque vous exécutez mvn test. Les cadres démarrent un conteneur grizzly. Vous pouvez utiliser jetty ou Tomcat en modifiant les dépendances.

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

Vous pouvez vérifier ce exemple d'application.

12
Fırat KÜÇÜK

Vous avez probablement écrit du code Java qui implémente votre logique métier et vous avez ensuite généré le point de terminaison des services Web pour cela.

Une chose importante à faire est de tester indépendamment votre logique métier. Puisqu'il est pur Java code, vous pouvez le faire avec des tests JUnit réguliers.

Maintenant, comme la partie des services Web n'est qu'un point final, ce que vous voulez vous assurer, c'est que la plomberie générée (stubs, etc.) est synchronisée avec votre code Java Java. Vous pouvez le faire). en écrivant des tests JUnit qui invoquent le service Web généré Java. Cela vous permettra de savoir quand vous modifiez vos signatures Java sans mettre à jour les services Web).

Si la plomberie de vos services Web est automatiquement générée par votre système de build à chaque build, il peut ne pas être nécessaire de tester les points de terminaison (en supposant que tout est correctement généré). Cela dépend de votre niveau de paranoïa.

6
entzik

Bien qu'il soit trop tard à partir de la date de publication de la question, j'ai pensé que cela pourrait être utile pour ceux qui ont une question similaire. Jersey est livré avec un framework de test appelé Jersey Test Framework qui vous permet de tester votre service Web RESTful, y compris les codes d'état de réponse. Vous pouvez l'utiliser pour exécuter vos tests sur des conteneurs légers comme Grizzly, HTTPServer et/ou EmbeddedGlassFish. En outre, le cadre pourrait être utilisé pour exécuter vos tests sur un conteneur Web standard comme GlassFish ou Tomcat.

6
Naresh

Jetez un oeil à générateur de client de repos Alchemy . Cela peut générer une implémentation de proxy pour votre classe de service Web JAX-RS en utilisant le client jersey en arrière-plan. En effet, vous vous appellerez des méthodes de service Web aussi simples que les méthodes Java issues de vos tests unitaires. Gère également l'authentification http.

Aucune génération de code n'est impliquée si vous devez simplement exécuter des tests, c'est donc pratique.

Avertissement: je suis l'auteur de cette bibliothèque.

3
Ashish Shinde

J'utilise Apache's HTTPClient (http://hc.Apache.org/) pour appeler Restful Services. La bibliothèque client HTTP vous permet d'effectuer facilement des opérations get, post ou toute autre opération dont vous avez besoin. Si votre service utilise JAXB pour la liaison xml, vous pouvez créer un JAXBContext pour sérialiser et désérialiser les entrées et sorties de la requête HTTP.

3
Chris Dail

Rester simple. Jetez un œil à https://github.com/valid4j/http-matchers qui peut être importé de Maven Central.

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

Exemple d'utilisation:

// 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...
2
keyoxy

Une chose importante à faire est de tester indépendamment votre logique métier

Je ne supposerais certainement pas que la personne qui a écrit le code JAX-RS et qui cherche à tester l'unité de l'interface est en quelque sorte, pour une raison bizarre et inexplicable, inconsciente de la notion qu'il ou elle peut tester en bloc d'autres parties du programme, y compris les classes de logique métier. Il n'est guère utile d'énoncer une évidence et il a été souligné à plusieurs reprises que les réponses devaient également être testées.

Jersey et RESTEasy ont tous deux des applications clientes et dans le cas de RESTEasy, vous pouvez utiliser les mêmes annotations (même factoriser l'interface annotée et utiliser du côté client et serveur de vos tests).

NE RESTEZ PAS ce que ce service peut faire pour vous; REST ce que vous pouvez faire pour ce service.

2
rickoshay

Si je comprends bien, le principal objectif de l'auteur de ce problème est de dissocier la couche JAX RS de celle de l'entreprise. Et test unitaire uniquement le premier. Ici, nous devons résoudre deux problèmes fondamentaux:

  1. Exécutez le test d'un serveur Web/d'application, mettez-y des composants JAX RS. Et seulement eux.
  2. Services commerciaux fictifs à l'intérieur des composants JAX RS/couche REST.

Le premier est résolu avec Arquillian. Le second est parfaitement décrit dans arquillican et mock

Voici un exemple de code, il peut différer si vous utilisez un autre serveur d'applications, mais j'espère que vous aurez l'idée de base et les avantages.

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

Quelques notes:

  1. La configuration JAX RS sans web.xml est utilisée ici.
  2. Le client JAX RS est utilisé ici (pas de RESTEasy/Jersey, ils exposent une API plus pratique)
  3. Lorsque le test démarre, le coureur d'Arquillian commence à travailler. Ici vous pouvez trouver comment configurer les tests pour Arquillian avec le serveur d'applications nécessaire.
  4. Selon le serveur d'application choisi, une URL dans le test diffère un peu. Un autre port peut être utilisé. 8181 est utilisé par Glassfish Embedded dans mon exemple.

J'espère que ça va aider.

1
Alexandr