Using JsonPath and XmlPath in REST Assured

While preparing my REST Assured workshop for the Romanian Testing Conference next month, I ran into a subject I feel I didn’t cover enough in the previous times I hosted the workshop: how to effectively use JsonPath and XmlPath to extract specific elements and element groups in RESTful API responses before verifying them. Here are a couple of tricks I learned since and worked into the exercises that’ll be part of the workshop from now on.

The examples in this post are all based on the following XML response:

<?xml version="1.0" encoding="UTF-8" ?>
<cars>
	<car make="Alfa Romeo" model="Giulia">
		<country>Italy</country>
		<year>2016</year>
	</car>
	<car make="Aston Martin" model="DB11">
		<country>UK</country>
		<year>1949</year>
	</car>
	<car make="Toyota" model="Auris">
		<country>Japan</country>
		<year>2012</year>
	</car>
</cars>

The syntax for JsonPath is very, very similar, except for the obvious lack of support for attributes in JsonPath (JSON does not have attributes).

Extracting a single element based on its index
Let’s get started with an easy example. Say I want to check that the first car in the list is made in Italy. To do this, we can simply traverse the XML tree until we get to the right element, using the index [0] to select the first car in the list:

@Test
public void checkCountryForFirstCar() {
						
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car[0].country", equalTo("Italy"));
}

Similarly, we can check that the last car came on the market in 2012, using the [-1] index (this points us to the last item in a list):

@Test
public void checkYearForLastCar() {
						
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car[-1].year", equalTo("2012"));
}

Extracting an attribute value
Just as easily, you can extract and check the value of an attribute in an XML document. If we want to check that the model of the second car in the list is ‘DB11’, we can do so using the ‘@’ notation:

@Test
public void checkModelForSecondCar() {
						
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car[1].@model", equalTo("DB11"));
}

Counting the number of occurrences of a specific value
Now for something a little more complex: let’s assume we want to check that there’s only one car in the list that is made in Japan. To do this, we’ll need to apply a findAll filter to the country element, and subsequently count the number of items in the list using size():

@Test
public void checkThereIsOneJapaneseCar() {
		
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car.findAll{it.country=='Japan'}.size()", equalTo(1));
}

Likewise, we can also check that there are two cars that are made either in Italy or in the UK, using the in operator:

@Test
public void checkThereAreTwoCarsThatAreMadeEitherInItalyOrInTheUK() {
		
	given().
	when().
		get("http://path.to/cars").
	then().
		assertThat().
		body("cars.car.findAll{it.country in ['Italy','UK']}.size()", equalTo(2));
}

Performing a search for a specific string of characters
Finally, instead of looking for exact attribute or element value matches, we can also filter on substrings. This is done using the grep() method (very similar to the Unix command). If we want to check the number of cars in the list whose make starts with an ‘A’, we can do so like this:

@Test
public void checkThereAreTwoCarsWhoseMakeStartsWithAnA() {
		
	given().
	when().
		get("http://localhost:9876/xml/cars").
	then().
		assertThat().
		body("cars.car.@make.grep(~/A.*/).size()", equalTo(2));
}

If you know of more examples, or if I missed another example of how to use JsonPath / XmlPath, do let me know!

(De)serializing POJOs in REST Assured

In this post, I’d like to demonstrate how you can leverage the ability of REST Assured to (de-)serialize Plain Old Java Objects (better known as POJOs) for more powerful testing.

As an example, we’ll use the following POJO representing a car and some of its properties:

public class Car {
	
	String make;
	String model;
	int year;
	
	public Car() {		
	}
	
	public Car(String make, String model, int year) {
		
		this.make = make;
		this.model = model;
		this.year = year;
	}
	
	public String getMake() {
		return this.make;
	}

	public void setMake(String make) {
		this.make = make;
	}
	
	public String toString() {
		return "My car is a " + this.year + " " + this.make + " " + this.model;
	}
}

Please note that I’ve removed the getters and setters for the other properties for brevity. Now, let’s create an instance of the car:

Car myCar = new Car("Aston Martin","DB9",2004);

Say we want to transmit the information stored in this object instance to a RESTful API, without having to map each individual property of our car to a corresponding field in the request. REST Assured supports this by allowing you to serialize the myCar object instance as follows:

@Test
public void testCarSerialization() {
				
	given().
		contentType("application/json").
		body(myCar).
	when().
		post("http://localhost:9876/carstub").
	then().
		assertThat().
		body(equalTo("Car has been stored"));
}

So, all you have to do is pass the object using body(). REST Assured will automatically translate this to the following request body:

{
    "make": "Aston Martin",
    "model": "DB9",
    "year": 2004
}

Neat, right? In this example, we serialized the car object to a JSON request, but REST Assured also allows you to serialize it to XML or HTML. Additionally, you can create custom object mappings as well. See this page in the REST Assured documentation for more information.

REST Assured also supports deserialization, meaning that we can easily transform a suitably formatted API response to a POJO instance:

@Test
public void testCarDeserialization() {
		
	Car myDeserializedCar = get("http://localhost:9876/carstub").as(Car.class);
		
	System.out.println(myDeserializedCar.toString());
	
	Assert.assertEquals("Check the car make", myDeserializedCar.getMake(), "Aston Martin");		
}

Note that http://localhost:9876/carstub points to a WireMock stub I’ve created to illustrate this example. The fact that our assertion is passing and that the console shows the following output when running the test tells us that deserialization has been successful:

My car is a 2004 Aston Martin DB9

You can download a Maven project containing all of the code I’ve used in the examples in this blog post here.

Open sourcing my workshop: an experiment

This is an experiment I’ve been thinking about for some time now. I have absolutely no idea how it will turn out, but hey, there’s only one way to find out!

What is an open source workshop?
I have recently created a workshop and delivered this with reasonable success to an audience of Dutch testers. I have received and processed the feedback I got that day, and now it’s time for the next step: I’m making it open source. This means that I’m giving everybody full access to the slides, the exercises and their solutions and the notes I have used in the workshop. I’m hoping this leads to:

  • Feedback on how to further improve the contents and the presentation of the workshop
  • More people delivering this workshop and spreading the knowledge it contains

What is the workshop about and who is it for?
The original version of this workshop is an introduction to RESTful web services and how to write tests for these web services using REST Assured. It is intended for testers with some basic Java programming skills and an interest in web service and API testing. However, as you start to modify the workshop to your personal preferences, both the contents and the target audience may of course change.

A simplified outline of the workshop looks like this:

  1. An introduction to RESTful web services and their use in modern IT applications
  2. An introduction to REST Assured as a tool to write tests for these services
  3. Setup of REST Assured and where to find documentation
  4. Introduction of the application under test
  5. Basic REST Assured features, Hamcrest and GPath
  6. Parameterization of tests
  7. Writing tests for secure web services
  8. Reuse: sharing variables between tests

The workshop comes with a couple of small (this workshop was originally designed to be delivered in 3 hours) sets of exercises and their solutions.

Prerequisites and installation instructions
As stated before, the workshop participants should have an interest in test automation, some basic Java (or any other object oriented programming language) knowledge as well as a grasp of the contents of web services.

The exercises are contained within a Maven project. As REST Assured is a Java library, in order to get things working you need a recent JDK and an IDE with support for Maven. Since TestNG is used as a test runner in the examples, your IDE should support this as well (see the TestNG web site for instructions), but feel free to rewrite the exercises to use your test framework of choice. I just happen to like TestNG.

So where can I find all these goodies?
That’s an easy one: on my GitHub repo.

How can I contribute?
I hope that by now you’re at least curious and maybe even enthusiastic about trying out this workshop and maybe even thinking of contributing to it. You can do so in various ways:

  • By giving me constructive feedback on either the concept of an open source workshop, the contents of this particular workshop, or both.
  • By spreading the word. I’d love for you to let your friends, colleagues or anyone else in your network know that this workshop exists. The more feedback I get, the better this workshop becomes.
  • By actually delivering the workshop yourself and letting me know how it turned out. That’s a great form of feedback!

Interesting, but I have some questions…
Sure! Just submit a comment to this post or send me an email at bas@ontestautomation.com. Especially if you’re planning to deliver the workshop, but feel you are stuck in some way. Also, please don’t forget to share the raving reviews you got (or stories on why and where you got booed off the stage..)! Remember that this is an experiment for me too, and if it turns out to be a successful one I definitely will create and publish more of these workshops in the future.

And on a final note, if you’re interested in having this workshop delivered at your organization, but you don’t feel comfortable doing it yourself, please let me know as well. I am happy to discuss the options available and work something out.