(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.

Selecting response elements with GPath in REST Assured

Hey, another post on REST Assured! This time I’d like to take a closer look at how you can select elements from a JSON or XML response to check their values. For this, REST Assured uses GPath, a path expression language integrated into the Groovy language. It is similar to XPath for XML, but GPath can handle both XML and JSON. This makes it an excellent fit for checking responses of RESTful web services. Read more on GPath here or here.

To see how GPath works and how you can use it effectively in REST Assured tests, let’s consider the following JSON response from the Ergast MRD API. This response lists all drivers for the 2016 Formula 1 season:

JSON response containing all Formula 1 drivers for the 2016 season

You can see the full response by selecting (or otherwise performing a GET call to) this link.

Extracting a single element value
Say we want to extract the driverId of the last driver and check that it is equal to wehrlein. Like with XPath, you can simply specify the path to the element starting with the root node and navigating through the tree until the required node is reached. Note that the index [-1] is used by GPath to denote the last matching element.

@Test
public void extractAndCheckSingleValue() {
		
	given().
	when().
		get("http://ergast.com/api/f1/2016/drivers.json").
	then().
		assertThat().
		body("MRData.DriverTable.Drivers.driverId[-1]",equalTo("wehrlein"));
}

Extracting a set of element values
Another check we might want to perform is that the collection of driverId values contains some specific values. This is done using a GPath expression very similar to the previous example:

@Test
public void extractAndCheckMultipleValues() {
		
	given().
	when().
		get("http://ergast.com/api/f1/2016/drivers.json").
	then().
		assertThat().
		body("MRData.DriverTable.Drivers.driverId",hasItems("alonso","button"));
}

Using the REST Assured base path
By now, you might have noticed that in every GPath expression we needed to start navigating from the root node MRData downwards. This results in quite long GPath expressions. REST Assured has a nifty little feature that can simplify these expressions by having you define a BasePath:

@BeforeTest
public void initPath() {
		
	RestAssured.rootPath = "MRData.DriverTable.Drivers";
}

This cleans up our tests and has a positive effect on maintainability:

@Test
public void extractAndCheckMultipleValues() {
		
	given().
	when().
		get("http://ergast.com/api/f1/2016/drivers.json").
	then().
		assertThat().
		body("driverId",hasItems("alonso","button"));
}

Extracting a specific subset of values
GPath also supports array slicing to retrieve specific subsets of a collection of values. In the example below, we check that the collection of elements with index [0] through [2] has exactly three items (the ones at index [0], [1] and [2]):

@Test
public void extractAndCheckArraySliceSize() {
		
	given().
	when().
		get("http://ergast.com/api/f1/2016/drivers.json").
	then().
		assertThat().
		body("driverId[0..2]",hasSize(3));
}

Filtering values
Using GPath, you can also filter values to return an even more specific subset of values. For example, if we only want to return the collection of permanentNumber values in use this year that are between 20 and 30 inclusive, we can do this:

@Test
public void extractAndCheckRange() {
		
	given().
	when().
		get("http://ergast.com/api/f1/2016/drivers.json").
	then().
		assertThat().
		body("findAll{Drivers->Drivers.permanentNumber >= \"20\" && Drivers.permanentNumber <= \"30\"}.permanentNumber",hasItem("22")).
		and().
		body("findAll{Drivers->Drivers.permanentNumber >= \"20\" && Drivers.permanentNumber <= \"30\"}.permanentNumber",not(hasItem("33")));
}

The quotes around the lower and upper boundary are required since the API call returns the permanentNumber values as strings instead of integers.

Use parameters
To make the previous example a little more flexible, we can parameterize both the lower and upper boundaries as well as the values that are expected to be (or not to be) in the resulting collection. Let’s specify these in a TestNG DataProvider first:

@DataProvider(name = "rangesAndValues")
public String[][] createTestDataObject() {
		
	return new String[][] {
		{"20","30","22","25"},
		{"30","40","33","31"},
		{"1","9","9","4"}
	};
}

Now we can apply these in our test:

@Test(dataProvider = "rangesAndValues")
public void extractAndCheckRangeParameterized(String lowerLimit, String upperLimit, String inCollection, String notInCollection) {
		
	given().
	when().
		get(getDriverListFor2016).
	then().		
		assertThat().
		body("findAll{Drivers->Drivers.permanentNumber >= \"" + lowerLimit + "\" && Drivers.permanentNumber <= \"" + upperLimit + "\"}.permanentNumber",hasItem(inCollection)).
		and().
		body("findAll{Drivers->Drivers.permanentNumber >= \"" + lowerLimit + "\" && Drivers.permanentNumber <= \"" + upperLimit + "\"}.permanentNumber",not(hasItem(notInCollection)));
}

Note that the syntax gets a little messy here, especially since we have to keep the escaped double quotes in the GPath expression. There might be an easier way to do this, but I haven’t found one that still supports the given()/when()/then() format. Any pointers on this are well appreciated!

Sample code
You can download a Maven project containing all the code examples presented in this blog post here.