Esplorazione del Jersey Test Framework

1. Panoramica

In questo tutorial, daremo un'occhiata al Jersey Test Framework e vedremo come possiamo usarlo per scrivere rapidamente test di integrazione.

Come abbiamo già visto negli articoli precedenti, Jersey è un framework open source per lo sviluppo di servizi Web RESTful . Possiamo saperne di più su Jersey nella nostra introduzione alla creazione di un'API con l'articolo Jersey e la primavera, qui.

2. Configurazione dell'applicazione

Il Jersey Test Framework è uno strumento che ci aiuta a verificare la corretta implementazione dei nostri componenti lato server. Come vedremo più avanti, fornisce un modo rapido e semplice per scrivere test di integrazione e può gestire molto bene la comunicazione con le nostre API HTTP.

Allo stesso modo, funziona quasi immediatamente ed è facile da integrare con i nostri progetti basati su Maven . Il framework si basa principalmente su JUnit sebbene sia possibile utilizzarlo anche con TestNG, il che lo rende utilizzabile in quasi tutti gli ambienti.

Nella sezione successiva, vedremo quali dipendenze dobbiamo aggiungere alla nostra applicazione per utilizzare il framework.

2.1. Dipendenze di Maven

Prima di tutto, aggiungiamo la dipendenza principale di Jersey Test Framework al nostro pom.xml :

 org.glassfish.jersey.test-framework jersey-test-framework-core 2.27 test 

Come sempre, possiamo ottenere l'ultima versione da Maven Central.

Quasi quasi tutti i test Jersey utilizzano la fabbrica di contenitori di prova Grizzly, che dovremo anche aggiungere:

 org.glassfish.jersey.test-framework.providers jersey-test-framework-provider-grizzly2 2.27 test 

Ancora una volta possiamo trovare l'ultima versione in Maven Central.

3. Introduzione

In questa sezione successiva, tratteremo i passaggi di base necessari per scrivere un semplice test.

Inizieremo testando la semplice risorsa Saluti sul nostro server:

@Path("/greetings") public class Greetings { @GET @Path("/hi") public String getHiGreeting() { return "hi"; } } 

3.1. Configurazione del test

Ora definiamo la nostra classe di test:

public class GreetingsResourceIntegrationTest extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(Greetings.class); } //... } 

Possiamo vedere nell'esempio sopra che per sviluppare un test utilizzando Jersey Test Framework il nostro test deve sottoclassare JerseyTest .

Successivamente, sovrascriviamo il metodo configure che restituisce una configurazione della risorsa personalizzata per il nostro test e contiene solo la risorsa Greetings . Questa è, ovviamente, la risorsa che desideriamo testare.

3.2. Scrivere il nostro primo test

Iniziamo testando una semplice richiesta GET dalla nostra API di saluto:

@Test public void givenGetHiGreeting_whenCorrectRequest_thenResponseIsOkAndContainsHi() { Response response = target("/greetings/hi").request() .get(); assertEquals("Http Response should be 200: ", Status.OK.getStatusCode(), response.getStatus()); assertEquals("Http Content-Type should be: ", MediaType.TEXT_HTML, response.getHeaderString(HttpHeaders.CONTENT_TYPE)); String content = response.readEntity(String.class); assertEquals("Content of ressponse is: ", "hi", content); } 

Si noti che abbiamo pieno accesso alla risposta HTTP, quindi possiamo fare cose come controllare il codice di stato per assicurarci che l'operazione sia andata a buon fine o lavorare con il corpo effettivo della risposta .

Spieghiamo più in dettaglio cosa facciamo nell'esempio sopra:

  1. Invia una richiesta HTTP GET a "/ greetings / hi"
  2. Controllare il codice di stato HTTP e le intestazioni di risposta del tipo di contenuto
  3. Testare il contenuto della risposta contiene la stringa "hi"

4. Test di GET per recuperare le risorse

Ora che abbiamo visto i passaggi di base coinvolti nella creazione dei test. Testiamo la semplice API Fruit che abbiamo introdotto nell'eccellente articolo del supporto Jersey MVC.

4.1. Ottieni Plain JSON

Nell'esempio seguente stiamo lavorando con il corpo della risposta come una stringa JSON standard:

@Test public void givenFruitExists_whenSearching_thenResponseContainsFruit() { final String json = target("fruit/search/strawberry").request() .get(String.class); assertThat(json, containsString("{\"name\":\"strawberry\",\"weight\":20}")); }

4.2. Ottieni entità invece di JSON

Possiamo anche mappare la risposta direttamente a una classe di entità Risorsa, ad esempio:

 @Test public void givenFruitExists_whenSearching_thenResponseContainsFruitEntity() { final Fruit entity = target("fruit/search/strawberry").request() .get(Fruit.class); assertEquals("Fruit name: ", "strawberry", entity.getName()); assertEquals("Fruit weight: ", Integer.valueOf(20), entity.getWeight()); }

Questa volta, specifichiamo il tipo Java in cui verrà convertita l'entità di risposta nel metodo get : un oggetto Fruit .

5. Test del POST per creare risorse

Per creare una nuova risorsa nella nostra API, faremo un buon uso delle richieste POST. Nella prossima sezione vedremo come testare questa parte della nostra API.

5.1. Pubblica JSON normale

Iniziamo pubblicando una semplice stringa JSON per testare la creazione di una nuova risorsa di frutta:

@Test public void givenCreateFruit_whenJsonIsCorrect_thenResponseCodeIsCreated() { Response response = target("fruit/created").request() .post(Entity.json("{\"name\":\"strawberry\",\"weight\":20}")); assertEquals("Http Response should be 201 ", Status.CREATED.getStatusCode(), response.getStatus()); assertThat(response.readEntity(String.class), containsString("Fruit saved : Fruit [name: strawberry colour: null]")); }

Nell'esempio precedente, utilizziamo il metodo post che accetta un parametro dell'oggetto Entity . Usiamo il comodo metodo json per creare un'entità dalla stringa JSON corrispondente .

5.2. Post Entity Instead of JSON

As we've already seen with get requests we can also post a Resource entity class directly – for example:

@Test public void givenCreateFruit_whenFruitIsInvalid_thenResponseCodeIsBadRequest() { Fruit fruit = new Fruit("Blueberry", "purple"); fruit.setWeight(1); Response response = target("fruit/create").request(MediaType.APPLICATION_JSON_TYPE) .post(Entity.entity(fruit, MediaType.APPLICATION_JSON_TYPE)); assertEquals("Http Response should be 400 ", 400, response.getStatus()); assertThat(response.readEntity(String.class), containsString("Fruit weight must be 10 or greater")); }

This time we use the entity method to post our Fruit entity and also specify the media type as JSON.

5.3. Form Submissions Using POST

In our final post example we will see how to test form submissions via a post request:

@Test public void givenCreateFruit_whenFormContainsNullParam_thenResponseCodeIsBadRequest() { Form form = new Form(); form.param("name", "apple"); form.param("colour", null); Response response = target("fruit/create").request(MediaType.APPLICATION_FORM_URLENCODED) .post(Entity.form(form)); assertEquals("Http Response should be 400 ", 400, response.getStatus()); assertThat(response.readEntity(String.class), containsString("Fruit colour must not be null")); }

Similarly, we make use of the Entity class but this time pass a form which contains a number of parameters to our post request.

6. Testing Other HTTP Verbs

Sometimes we need to test other HTTP endpoints such as PUT and DELETE. This is of course perfectly possible using the Jersey Test Framework.

Let's see a simple PUT example:

@Test public void givenUpdateFruit_whenFormContainsBadSerialParam_thenResponseCodeIsBadRequest() { Form form = new Form(); form.param("serial", "2345-2345"); Response response = target("fruit/update").request(MediaType.APPLICATION_FORM_URLENCODED) .put(Entity.form(form)); assertEquals("Http Response should be 400 ", 400, response.getStatus()); assertThat(response.readEntity(String.class), containsString("Fruit serial number is not valid")); }

Once we have called the request method, we can invoke any HTTP method on the current request object.

7. Additional Features

The Jersey test framework contains a number of additional configuration properties which can help aid debugging and testing.

In the next example we'll see how to programmatically enable a feature with a given name:

public class FruitResourceIntegrationTest extends JerseyTest { @Override protected Application configure() { enable(TestProperties.LOG_TRAFFIC); enable(TestProperties.DUMP_ENTITY); //...

When we create and configure our Jersey application under test. We can also enable additional properties. In this case, we enable two logging properties – LOG_TRAFFIC and DUMP_ENTITYwhich will provide useful additional logging and debug information during test runs.

8. Supported Containers

As we've already mentioned the defacto container used when writing tests with the Jersey Test Framework is Grizzly. However, a number of other containers are supported:

  • In-Memory container
  • HttpServer from Oracle JDK
  • Simple container (org.simpleframework.http
  • Jetty container (org.eclipse.jetty)

For more information on how to configure these containers, please see the documentation here.

9. Conclusion

To summarize, in this tutorial, we’ve explored the Jersey Test Framework. First, we started by introducing how to configure the Jersey Test Framework and then we saw how to write a test for a very simple API.

In the next section, we saw how to write tests for a variety of GET and POST API endpoints. Finally, we looked at some additional features and the containers that the Jersey Test Framework supports.

Come sempre, il codice sorgente completo dell'articolo è disponibile su GitHub.