Prova un'API REST con Java

1. Panoramica

Questo tutorial si concentra sui principi e sui meccanismi di base del test di un'API REST con test di integrazione live (con un payload JSON).

L'obiettivo principale è fornire un'introduzione al test della correttezza di base dell'API e per gli esempi utilizzeremo l'ultima versione dell'API REST di GitHub.

Per un'applicazione interna, questo tipo di test viene solitamente eseguito come un passaggio avanzato in un processo di integrazione continua, utilizzando l'API REST dopo che è già stata distribuita.

Quando si testa una risorsa REST, di solito ci sono alcune responsabilità ortogonali su cui i test dovrebbero concentrarsi:

  • il codice di risposta HTTP
  • altre intestazioni HTTP nella risposta
  • il payload (JSON, XML)

Ogni test dovrebbe concentrarsi solo su una singola responsabilità e includere una singola asserzione. Concentrarsi su una netta separazione ha sempre dei vantaggi, ma quando si esegue questo tipo di test della scatola nera è ancora più importante, poiché la tendenza generale è quella di scrivere scenari di test complessi all'inizio.

Un altro aspetto importante dei test di integrazione è l'aderenza al principio del livello unico di astrazione : la logica all'interno di un test dovrebbe essere scritta ad un livello elevato. Dettagli come la creazione della richiesta, l'invio della richiesta HTTP al server, la gestione dell'IO, ecc. Non dovrebbero essere eseguiti in linea ma tramite metodi di utilità.

2. Verifica del codice di stato

@Test public void givenUserDoesNotExists_whenUserInfoIsRetrieved_then404IsReceived() throws ClientProtocolException, IOException { // Given String name = RandomStringUtils.randomAlphabetic( 8 ); HttpUriRequest request = new HttpGet( "//api.github.com/users/" + name ); // When HttpResponse httpResponse = HttpClientBuilder.create().build().execute( request ); // Then assertThat( httpResponse.getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_NOT_FOUND)); }

Questo è un test piuttosto semplice: verifica che un percorso felice di base funzioni , senza aggiungere troppa complessità alla suite di test.

Se per qualsiasi motivo fallisce, non è necessario esaminare nessun altro test per questo URL finché non viene risolto.

3. Verifica del tipo di supporto

@Test public void givenRequestWithNoAcceptHeader_whenRequestIsExecuted_thenDefaultResponseContentTypeIsJson() throws ClientProtocolException, IOException { // Given String jsonMimeType = "application/json"; HttpUriRequest request = new HttpGet( "//api.github.com/users/eugenp" ); // When HttpResponse response = HttpClientBuilder.create().build().execute( request ); // Then String mimeType = ContentType.getOrDefault(response.getEntity()).getMimeType(); assertEquals( jsonMimeType, mimeType ); }

Ciò garantisce che la risposta contenga effettivamente dati JSON.

Come avrai notato, stiamo seguendo una progressione logica dei test : prima il codice di stato della risposta (per garantire che la richiesta fosse OK), quindi il tipo di supporto della risposta e solo nel test successivo esamineremo il carico utile JSON effettivo.

4. Test del payload JSON

@Test public void givenUserExists_whenUserInformationIsRetrieved_thenRetrievedResourceIsCorrect() throws ClientProtocolException, IOException { // Given HttpUriRequest request = new HttpGet( "//api.github.com/users/eugenp" ); // When HttpResponse response = HttpClientBuilder.create().build().execute( request ); // Then GitHubUser resource = RetrieveUtil.retrieveResourceFromResponse( response, GitHubUser.class); assertThat( "eugenp", Matchers.is( resource.getLogin() ) ); }

In questo caso, so che la rappresentazione predefinita delle risorse GitHub è JSON, ma di solito l' intestazione Content-Type della risposta deve essere testata insieme all'intestazione Accept della richiesta: il client richiede un particolare tipo di rappresentazione tramite Accept , che il server dovrebbe onorare.

5. Utilità per il test

Useremo Jackson 2 per unmarshalling della stringa JSON non elaborata in un'entità Java indipendente dai tipi:

public class GitHubUser { private String login; // standard getters and setters }

Usiamo solo una semplice utility per mantenere i test puliti, leggibili e con un alto livello di astrazione:

public static  T retrieveResourceFromResponse(HttpResponse response, Class clazz) throws IOException { String jsonFromResponse = EntityUtils.toString(response.getEntity()); ObjectMapper mapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return mapper.readValue(jsonFromResponse, clazz); }

Si noti che Jackson sta ignorando le proprietà sconosciute che l'API GitHub ci sta inviando - è semplicemente perché la rappresentazione di una risorsa utente su GitHub diventa piuttosto complessa - e non abbiamo bisogno di nessuna di queste informazioni qui.

6. Dipendenze

Le utilità ei test fanno uso delle seguenti librerie, tutte disponibili in Maven central:

  • HttpClient
  • Jackson 2
  • Hamcrest (opzionale)

7. Conclusione

Questa è solo una parte di ciò che dovrebbe essere la suite di test di integrazione completa. I test si concentrano sull'assicurare la correttezza di base per l'API REST , senza entrare in scenari più complessi,

Ad esempio, quanto segue non è coperto: rilevabilità dell'API, consumo di diverse rappresentazioni per la stessa risorsa, ecc.

L'implementazione di tutti questi esempi e frammenti di codice può essere trovata su Github: questo è un progetto basato su Maven, quindi dovrebbe essere facile da importare ed eseguire così com'è.