Ga naar hoofdinhoud

Spring Boot applicatie testen

Om een Spring boot API (applicatie) met Gherkin scenario's te kunnen testen is er de aanvullende uitdaging om vanuit glue code de juiste endpoints aan te kunnen roepen zodat het gedrag (van de API) getest kan worden.

Maak hiervoor bijvoorbeeld gebruik van RestAssured dat dit mogelijk maakt met een fluent interface.

Voor een wat uitgebreidere introductie op de library zou je dit artikel kunnen lezen, maar het belangrijkste om te realiseren is dat het testen van een (REST) API meestal bestaat uit twee stappen:

  1. uitvoeren van een HTTP request (GET/POST/UPDATE/DELETE)
  2. afhandelen van de HTTP response (valideren van HTTP status code, valideren van HTTP bericht)

Hoe je deze handelingen kunt bereiken is hieronder geïllustreerd met wat simpele voorbeelden van een GET- en POST-request.

HTTP request

Een eenvoudig voorbeeld van een GET-request met RestAssured

Response response = RestAssured
.given()
.when()
.get("https://api.example.com/users")
.then()
.extract()
.response();

Een eenvoudig voorbeeld van een POST-request inclusief dummy JSON (HTTP body) met RestAssured

Response response = RestAssured
.given()
.contentType(ContentType.JSON)
.body("{\"query\":\"test\"}")
.when()
.post("https://recipe-food-nutrition15.p.rapidapi.com/recipee-search")
.then()
.extract()
.response();

HTTP response

Een HTTP status code kan door RestAssured gecheckt worden voordat de inhoud opgehaald wordt om fouten verderop in de afhandeling te voorkomen. Dit is simpelweg mogelijk door voordat de functie extract aangeroepen wordt op de interface allereerst de verwachte status code te verifiëren.

Response response = RestAssured
.given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200) // verwachte status code
.extract()
.response();

Het resultaat van de extract.response is dat er een object is met alle data uit het ontvangen HTTP bericht als antwoord op de request. Dit response object zou je kunnen opslaan in een variabele van de StepDefinitions.java om tijdens een Cucumber @Then stap te verifiëren.

De response kan inhoudelijk gequeried worden met bijvoorbeeld JSONPath door deze uit de response te extraheren met de bijbehorende functie. Vervolgens is het mogelijk om specifieke velden uit de JSON uit te lezen of om te zetten naar objecten middels Object Mapping. Zie de documentatie van JSONPath of gebruik deze online editor om de juiste queries op te stellen.

Zie deze pagina voor een voorbeeld met automatische mapping naar een object om een idee te krijgen hoe dit werkt. Ter illustratie is ook een snippet van de JSON die in de HTTP response terug zou komen opgenomen ter referentie. Zoals je kunt zien wordt door middel van JSONPath query de JSON data afgewandeld tot de recepten waarvan de velden met JSON annotaties gekoppeld worden aan attributen in het object.

Default API URI en port configuratie

Een aantal instellingen die bij elke HTTP request terugkomt zijn de URI en poort waarop deze benaderd dient te worden. Hiervoor is een handige annotatie ontwikkeld in het geval deze door Spring Boot opgestart is, een variabele geannoteerd met @LocalServerPort zal altijd automatisch gevuld worden met de poort van de lokale Spring Boot server.

Aannemende dat de application.properties van de Spring applicatie zo geconfigureerd is om op localhost te draaien, krijg je onderstaande code. Die gebruikt kan worden om in een functie een specifiek endpoint van de applicatie onder test (in dit geval "users") aan te roepen.

@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// TODO: moet ook zonder de context na elke test opnieuw op te bouwen kunnen, maar hoe?
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class StepDefinitions {

@LocalServerPort
private int port;

@Before
public void configureRestAssured() {
RestAssured.baseURI = "http://localhost";
RestAssured.port = port;
}

private Response exampleAPICall(){
return RestAssured
.given()
.when()
.get("/users")
.then()
.extract()
.response();
}
}