Omdat het moeilijk was om een eenvoudige tutorial te vinden voor rest-assured.io, ben ik na eerst zelf uit te vinden hoe het werkt, maar eens begonnen met een tutorial die de absolute basics uitlegt over rest-assured. In deze post laat ik zien hoe we op een zo eenvoudig mogelijke manier een API test kunnen maken met behulp van Java en rest-assured.io.
Ik gebruik hiervoor IntelliJ IDEA 2107.1.5 Community, standaard installatie met Maven (komt mee met IntelliJ) en Java SDK 1.8 en werk onder Windows 10.
Setup
In IntelliJ starten we met een leeg Maven project met standaard instellingen.
In het nieuwe project zetten we de nieuwe class files in de src/test/java folder en de dependencies in de pom.xml file.
Open je pom.xml file en voeg het volgende toe in de <project> node om met rest-assured te kunnen beginnen:
<dependencies>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.0.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.8</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
Mooi, we zijn voorbereid en kunnen van start. Je nieuwe classes zal je voor nu willen plaatsen onder src/test/java. Voeg een nieuwe class toe onder src/test/java (rechtermuistoets, Add -> new class), dit wordt je testclass (de naam maakt niet uit je kan hem later altijd nog renamen met shift + F6).
Voor onze eerste testen gebruiken we een eenvoudige API. Later zal je dingen willen doen met authenticatie en wellicht sessies en cookies, maar laten we simpel beginnen. Een simpele API vind je bijvoorbeeld op https://jsonplaceholder.typicode.com/. Zij zijn ook de makers van de json-server waar we later kennis mee gaan maken.
Kijk maar even rond op die site. Je kan bijvoorbeeld naar de URL https://jsonplaceholder.typicode.com/posts (<- probeer maar!) gaan om alle blogposts uit de fake-blog api te halen of https://jsonplaceholder.typicode.com/posts/42 om een enkele blogpost op te halen.
Eerste Test
In onze eerste test gaan we kijken of we connectie kunnen maken met de API. Voeg de volgende methode aan je test class toe:
@Test
public void getTheFirstPost() {
when()
.get("https://jsonplaceholder.typicode.com/posts/1")
.then()
.statusCode(200);
}
Je zal wat rode kringels krijgen omdat je de imports mist van de rest-assured libraries, laat IntelliJ dat voor je oplossen met de Alt+Enter shortcut.
Run de test en als het goed is slaagt hij.
Het is handig om de setup in je constructor opzet zodat je dit maar eenmalig hoeft te doen. In dit geval alleen de basic URL, maar je kan hier ook je authenticatie, ports etc. in kwijt. Zie de rest-assured docs voor meer.
Ik heb mijn class jsonPlaceHolderTests genoemd, dus mijn constructor heet ook zo:
public class jsonPlaceHolderTests {
public jsonPlaceHolderTests() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
}
Nu hoef je voortaan niet meer de hele URL op te geven, maar alleen de api branch waar je in geinteresseerd bent, in ons geval:
@Test
public void getTheFirstPost() {
when()
.get("posts/1")
.then()
.statusCode(200) .log().all();
}
Vaak zal je een given() method gebruiken voor de when() methode, in dit geval is het niet nodig. Een andere verandering is dat we de respons loggen naar de console. Zo kan je zien wat er terug komt aan headers en body:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Body Check
In de volgende test kijken we niet naar de repsons code, maar naar de inhoud. Laten we eens kijken of we een assert kunnen schrijven op de userId:
@Test
public void postShouldHaveUser() {
when()
.get("posts/1")
.then()
.body("userId", equalTo(1));
}
De equalTo is onderdeel van de HamCrest Matchers collectie, een volledige omschrijving vind je hier: http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html
Zo kunnen we eenvoudig bekijken of de titel niet leeg is met de anything matcher:
@Test
public void postShouldHaveTitle() {
when()
.get("posts/1")
.then()
.body("title", anything());
}
Of als we willen kijken of alle blog-posts titel bevatten die niet leeg is:
@Test
public void allPostsShouldHaveAPopulatedTitle() {
when()
.get("posts")
.then()
.body("title", everyItem(anything()))
}
Hier krijgen we een vrij eenvoudig JSON object terug. Als je een complex object terug krijgt kan je specificeren welk gedeelte je wilt testen met de body en wat je daaruit wilt testen met de matchers. De body is het path naar het object toe zoals je dat ook zou doen met een JavaScript object. Het juiste path te pakken krijgen vergt enige oefening zoals met elke path-traversal tool.
Toevoegen en manipuleren van data
Natuurlijk willen we niet alleen informatie opvragen, maar ook toevoegen, wijzigen of verwijderen. Voordat we verder gaan met post request, zetten we eerst onze eigen JSON server op. Als je nodejs hebt geinstalleerd, heb je automatisch npm waarmee je makkelijk de json server binnen kan halen met:
npm install -g json-server
Je start de json-server met de volgende commandline. Dit neemt de default db, je kan uiteraard je eigen db maken:
json-server --watch db.json
als het goed is zal je nu zien dat je json server op port 3000 van je localhost werkt:
We maken een aparte class aan voor tests op deze nieuwe omgeving en een test om te kijken of we een resultaat terug kunnen krijgen:
public class jsonServerTests {
public jsonServerTests() {
RestAssured.baseURI = "http://localhost";
RestAssured.port = 3000;
}
@Test
public void firstPostCanBeRetrieved() {
when()
.get("posts/1")
.then()
.statusCode(200)
.log().all();
}
}
als het goed is moet deze test slagen.
Nu gaan we een nieuwe blog-post ‘posten’. Het posten kost iets meer werk, want we moeten een content type opgeven en de content zelf. De content geven we mee als een HashMap waarvan de values in dit geval simpele strings zijn, maar die natuurlijk ook Arrays of nieuwe HashMaps kunnen zijn.
@Test
public void newPostCanBeCreated() {
Map<String, Object> jsonAsMap = new HashMap<String, Object>();
jsonAsMap.put("title", "My Favorite Towel");
jsonAsMap.put("author", "Arthur Dent");
given()
.contentType(JSON)
.body(jsonAsMap)
.when()
.post("/posts")
.then()
.statusCode(201)
.log().all();
}
Let op dat je een statuscode 201 terugkrijgt en niet 200.
Als je terugkijkt in de db.json file waar json-server in draait, zie je dit object als het goed is weer terug (dit zie je ook terug in de testlog omdat we nog steeds .log().all() gebruiken):
{
"author": "Arthur Dent",
"title": "My Favorite Towel",
"id": 2
}
Kunnen we een item ook deleten? In onze test gaan we eerst een item aanmaken die we meteen weer verwijderen. Tijdens het aanmaken willen we de id terugkrijgen en wellicht ook de andere properties van de post. Dat kunnen we doen door in de then fase de response te extracten:
Response response =
given()
.contentType(JSON)
.body(jsonAsMap)
.when()
.post("/posts")
.then()
.extract().response();
De response kunnen we als string uitlezen met behulp van asString(), maar dan moeten we met string manipulaties de Id eruit vogelen. Het zou handiger zijn als we het gelijk om konden zetten in een object en laat dat nou net mogelijk zijn.
Als eerste maken we een eenvoudige POJO:
public class SitePost {
private int id;
private String title;
private String author;
public void setAuthor(String author) {
this.author = author;
}
public void setTitle(String title) {
this.title = title;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
}
En nu moeten we de response omzetten naar dit een SitePost object. Dat gaat verbazingwekkend eenvoudig:
SitePost sitePost = response.as(SitePost.class);
Het deleten gaat op Id en je krijgt weer een 200 als response code:
when()
.delete("/posts/{0}", sitePost.getId())
.then()
.statusCode(200);
De hele test ziet er dus als volgt uit:
@Test
public void oldPostsCanBeCreatedDeleted() {
Map<String, Object> jsonAsMap = new HashMap<String, Object>();
jsonAsMap.put("title", "To Be Deleted");
jsonAsMap.put("author", "Flut Schrijver");
// setup a deletable post
Response response =
given()
.contentType(JSON)
.body(jsonAsMap)
.when()
.post("/posts")
.then()
.extract().response();
SitePost sitePost = response.as(SitePost.class);
// delete it
when()
.delete("/posts/{0}", sitePost.getId())
.then()
.statusCode(200);
}
Conclusie
Hoewel er veel libraries zijn die je kan gebruiken voor rest api calls, is rest-assured.io juist specifiek gericht op testen. Het werkt via de given-when-then triple-A aanpak wat je afdwingt om je testen structureel op te pakken. Het is redelijk eenvoudig om snel een test op te zetten, als zal je bij een wat complexere configuratie ook wat meer moeten configureren met rest-assured.
Het doorlopen van de json paden is soms even puzzelen, maar in combinatie met de HamCrest matchers geeft dit een krachtige toolkit om je checks te doen. Ook hebben we in dit voorbeeld laten zien hoe je een respons om kan zetten in een Java object.
Er is nog veel meer mogelijk met deze library en daar ligt eigenlijk het heikele punt: er is niet zo heel veel documentatie beschikbaar. Dat was ook een van de redenen om juist een zo eenvoudig mogelijke tutorial hier neer te zetten. Als je deze toolkit wilt gebruiken moet je dus wel wat tijd investeren om het volledig te doorgronden.
Comments