Stai testando un servizio Web JAX-RS?


84

Attualmente sto cercando modi per creare test automatizzati per un servizio web basato su JAX-RS (Java API for RESTful Web Services).

Fondamentalmente ho bisogno di un modo per inviargli determinati input e verificare di ottenere le risposte previste. Preferirei farlo tramite JUnit, ma non sono sicuro di come farlo.

Quale approccio utilizzi per testare i tuoi servizi web?

Aggiornamento: come ha sottolineato entzik, il disaccoppiamento del servizio Web dalla logica aziendale mi consente di eseguire un test unitario della logica aziendale. Tuttavia, voglio anche testare i codici di stato HTTP corretti, ecc.


6
Bella domanda, tuttavia direi che se stai testando su HTTP, mi colpisce che si tratta di test di integrazione.
Tom Duckering

Tom. Hai assolutamente ragione. Dovremmo iniettare un emulatore HTTP fittizio / contenitore leggero per questo. Nel mondo node.js supertest fa questo. Puoi emulare express.js.
Fırat KÜÇÜK

Risposte:


34

Jersey è dotato di un'ottima API client RESTful che semplifica la scrittura di unit test. Vedi gli unit test negli esempi forniti con Jersey. Usiamo questo approccio per testare il supporto REST in Apache Camel , se sei interessato i casi di test sono qui


6
re: now bad link Puoi trovare gli esempi menzionati nei jersey / samples che mostrano i test unitari, fondamentalmente usando i consumatori di jersey per consumare le risorse web. download.java.net/maven/2/com/sun/jersey/samples/bookstore/…
rogerdpack

2
Questo progetto è su GitHub, trova i test nella cartella src / test: github.com/jersey/jersey/tree/master/examples/bookstore-webapp
Venkat

2
Non dubito di questa risposta, ma trovo incredibilmente divertente che Jersey si faccia sempre strada in una conversazione JAX-RS, quando in alcuni casi (WebSphere, sfortunatamente, per essere esatti) non è disponibile e rende il 99% di tutte le risposte accettabili su Stack Overflow null and void.

26

Puoi provare REST Assured che rende molto semplice testare i servizi REST e convalidare la risposta in Java (utilizzando JUnit o TestNG).


1
Ho votato a favore del tuo post perché la biblioteca sembrava buona, ma sicuramente usano molti barattoli dipendenti ...
Perry Tew

18

Come ha detto James; C'è built-in framework di test integrato per Jersey. Un semplice esempio di Hello World può essere così:

pom.xml per l'integrazione con Maven. Quando corri mvn test. I framework iniziano un contenitore grizzly. Puoi usare jetty o tomcat cambiando le dipendenze.

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

Puoi controllare questa applicazione di esempio.


Con Jersey 2.29.1, ho dovuto aggiungere jersey-hk2come dipendenza perché ricevevo un java.lang.IllegalStateException: InjectionManagerFactoryerrore non trovato (vedi questa domanda ). Altrimenti questo esempio funziona bene.
Sarah N

7

Probabilmente hai scritto del codice java che implementa la tua logica aziendale e quindi hai generato l'endpoint dei servizi web per questo.

Una cosa importante da fare è testare in modo indipendente la logica aziendale. Poiché è puro codice java, puoi farlo con normali test JUnit.

Ora, poiché la parte dei servizi web è solo un punto finale, quello che vuoi assicurarti è che l'impianto idraulico generato (stub, ecc.) Sia sincronizzato con il tuo codice java. puoi farlo scrivendo test JUnit che invocano i client java del servizio web generato. Questo ti farà sapere quando modifichi le tue firme java senza aggiornare il materiale dei servizi web.

Se l'impianto idraulico dei servizi Web viene generato automaticamente dal sistema di compilazione ad ogni compilazione, potrebbe non essere necessario testare gli endpoint (supponendo che sia tutto generato correttamente). Dipende dal tuo livello di paranoia.


2
Hai ragione, anche se devo anche testare le risposte HTTP effettive che vengono restituite, in particolare i codici di stato HTTP.
Einar

6

Sebbene sia troppo tardi dalla data di pubblicazione della domanda, ho pensato che questo potrebbe essere utile per altri che hanno una domanda simile. Jersey viene fornito con un framework di test denominato Jersey Test Framework che consente di testare il servizio Web RESTful, inclusi i codici di stato della risposta. Puoi usarlo per eseguire i tuoi test su contenitori leggeri come Grizzly, HTTPServer e / o EmbeddedGlassFish. Inoltre, il framework potrebbe essere utilizzato per eseguire i test su un normale contenitore web come GlassFish o Tomcat.


Hai un buon esempio su come simulare i gestori di chiamate? JerseyHttpCall -> MyResource -> CallHandler.getSomething () Come possiamo deridere CallHandler qui?
Balaji Boggaram Ramanarayan

3

Uso HTTPClient di Apache (http://hc.apache.org/) per chiamare Restful Services. La libreria client HTTP ti consente di eseguire facilmente get, post o qualsiasi altra operazione di cui hai bisogno. Se il servizio utilizza JAXB per l'associazione xml, è possibile creare un JAXBContext per serializzare e deserializzare input e output dalla richiesta HTTP.


3

Dai un'occhiata al generatore di client di riposo Alchemy . Questo può generare un'implementazione proxy per la tua classe di servizio web JAX-RS utilizzando il client jersey dietro le quinte. Effettivamente ti chiamerai metodi webservice come semplici metodi java dai tuoi unit test. Gestisce anche l'autenticazione http.

Non è prevista la generazione di codice se è necessario eseguire semplicemente dei test, quindi è conveniente.

Dichiarazione di non responsabilità: sono l'autore di questa libreria.


2

Sii semplice. Dai un'occhiata a https://github.com/valid4j/http-matchers che possono essere importati da Maven Central.

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

Esempio di utilizzo:

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

1

Una cosa importante da fare è testare in modo indipendente la logica aziendale

Certamente non presumo che la persona che ha scritto il codice JAX-RS e sta cercando di testare l'interfaccia dell'interfaccia sia in qualche modo, per qualche motivo bizzarro e inspiegabile, ignaro dell'idea che lui o lei possa testare altre parti del programma, comprese le lezioni di logica aziendale. Non è affatto utile affermare l'ovvio e si è ripetutamente ribadito che anche le risposte devono essere testate.

Sia Jersey che RESTEasy hanno applicazioni client e nel caso di RESTEasy puoi usare gli stessi annunci (anche escludere l'interfaccia annotata e usarla sul lato client e server dei tuoi test).

NON RIPOSO quello che questo servizio può fare per te; RIPOSA cosa puoi fare per questo servizio.


Le persone potrebbero voler testare alcune preoccupazioni trasversali. Ad esempio convalida, autenticazione, intestazioni HTTP desiderate, ecc. Quindi le persone possono preferire testare il proprio codice JAX-RS.
Fırat KÜÇÜK

Nella mia applicazione, utilizzo ModelMapper per "mappare" dalle classi "DTO" alle classi "oggetti business", che sono comprese dalle classi "servizio" sottostanti. Questo è un esempio di qualcosa che sarebbe bene testare in modo indipendente.
jkerak

E a volte l'applet REST ha così poca complessità che i mock sarebbero più grandi del livello dell'applicazione, come nel mio caso attuale. :)
tekHedd

1

Da quanto ho capito, lo scopo principale dell'autore di questo problema è di separare il livello JAX RS da quello aziendale. E unit test solo il primo. Qui dobbiamo risolvere due problemi fondamentali:

  1. Esegui in prova alcuni server web / applicazioni, inserisci i componenti JAX RS in esso. E solo loro.
  2. Servizi aziendali simulati all'interno dei componenti JAX RS / livello REST.

Il primo è risolto con Arquillian. Il secondo è perfettamente descritto in arquillicano e beffardo

Di seguito è riportato un esempio del codice, potrebbe differire se si utilizza un altro server delle applicazioni, ma spero che tu possa ottenere l'idea di base ei vantaggi.

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

Un paio di note:

  1. La configurazione JAX RS senza web.xml viene utilizzata qui.
  2. Qui viene utilizzato il client JAX RS (non RESTEasy / Jersey, espongono API più convenienti)
  3. Quando inizia il test, il corridore di Arquillian inizia a lavorare. Qui puoi trovare come configurare i test per Arquillian con il server delle applicazioni necessario.
  4. A seconda del server delle applicazioni scelto, un URL nel test sarà leggermente diverso. È possibile utilizzare un'altra porta. 8181 è utilizzato da Glassfish Embedded nel mio esempio.

Spero che ti aiuti.


questo tipo di cose è supportato dal framework di test jersey integrato? Sono solo riluttante ad aggiungere "ancora un altro quadro" e tutti i suoi barattoli al mio progetto se ho già qualcosa disponibile con Jersey.
jkerak
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.