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.

Creating data driven API tests with REST Assured and TestNG

As you’ve probably read in previous posts on this site, I am a big fan of the REST Assured library for writing tests for RESTful web services. In this post, I will demonstrate how to create even more powerful tests with REST Assured by applying the data driven principle, with some help from TestNG.

Combining REST Assured and TestNG
REST Assured itself is a Domain Specific Language (DSL) for writing tests for RESTful web services and does not offer a mechanism for writing data driven tests (i.e., the ability to write a single test that can be executed multiple times with different sets of input and validation parameters). However, REST Assured tests are often combined with JUnit or TestNG, and the latter offers an easy to use mechanism to create data driven tests through the use of the DataProvider mechanism.

Creating a TestNG DataProvider
A TestNG DataProvider is a method that returns an object containing test data that can then be fed to the actual tests (REST Assured tests in this case). This data can be hardcoded, but it can also be read from a database or a JSON specification, for example. It’s simply a matter of implementing the DataProvider in the desired way. The following example DataProvider creates a test data object (labeled md5hashes) that contains some example text strings and their md5 hash:

@DataProvider(name = "md5hashes")
public String[][] createMD5TestData() {
		
	return new String[][] {
			{"testcaseOne", "4ff1c9b1d1f23c6def53f957b1ed827f"},
			{"testcaseTwo", "39738347fb533d798aca9ae0f56ca126"},
			{"testcaseThree", "db6b151bb4bde46fddb361043bc3e2d9"}
	};
}

Using the DataProvider in REST Assured tests (with query parameters)
Using the data specified in the DataProvider in REST Assured tests is fairly straightforward. In the example below, we’re using it in combination with a RESTful web service that uses a single query parameter:

@Test(dataProvider = "md5hashes")
public void md5JsonTest(String originalText, String md5Hash) {
		
	given().
		parameters("text", originalText).
	when().
		get("http://md5.jsontest.com").
	then().
		assertThat().
		body("md5", equalTo(md5Hash));
}

Notice how the data in the DataProvider can be used for specification of expected results just as easy as for specifying input parameters. When we run this test, we can see in the output that the test is run three times, once for every set of input and validation parameters:

Console output for our data driven REST Assured test

Using the DataProvider in REST Assured tests (with query parameters)
The other parameter approach commonly used with RESTful web services is the use of path parameters. Let’s create another DataProvider, this one specifying Formula 1 circuits and the country they are situated in:

@DataProvider(name = "circuitLocations")
public String[][] createCircuitTestData() {
		
	return new String[][] {
			{"adelaide","Australia"},
			{"detroit","USA"},
			{"george","South Africa"}
	};
}

The Ergast Developer API provides motor racing data through a public API (check it out, it’s really cool). Here’s how to parameterize the call to get specific circuit data and check the country for each circuit with REST Assured, again using our DataProvider:

@Test(dataProvider = "circuitLocations")
public void circuitLocationTest(String circuitId, String location) {
		
	given().
		pathParameters("circuitId",circuitId).
	when().
		get("http://ergast.com/api/f1/circuits/{circuitId}.json").
	then().
		assertThat().
		body("MRData.CircuitTable.Circuits[0].Location.country",equalTo(location));
}

The only real difference is in how to set up path parameter use in REST Assured (see also here), otherwise making your tests data driven is just as easy as with query parameters.

When we run this second test, we can see again that it’s run three times, once for every circuit specified:

Console output for our data driven REST Assured test (with path parameters_

A Maven Eclipse project containing the code demonstrated in this blog post can be downloaded here.

Three reasons to start improving your API test automation skills

Modern applications and software development methods have changed the requirements for testers and the skills they need to possess to add real value to their clients and projects. One of these emerging and sought after skills is the ability to design and execute automated tests for APIs. In this post, I will give you three reasons why it might be useful for you to start improving your API test automation skills.

API word cloud

APIs are everywhere
The first reason why you should invest in your API test automation skills is a simple question of demand and supply: APIs are becoming ever more present in current IT solutions. From mobile applications to the Internet of Things, many modern systems and applications expose their data, their business logic or both through APIs. Whether you’re building an application that uses APIs to expose data or logic to the outside world, or you’re on the other side as an API consumer, you need to be able to perform tests on APIs. Otherwise, how are you going to ensure that an API and its integration with the outside world function as expected?

Oh, and if you’re testing an application that consumes a third-party API, please don’t fall into the trap of assuming that this API you’re using works perfectly as designed, or that integration with your own application will be seamless. Can anyone really assure you that that business-critical third party API you’re relying on has been tested for your specific situation and requirements? Thought not. Make sure it does and do some proper testing on it!

API test automation hits the sweet spot between speed and coverage
The second reason why API test automation can be very useful is that automated checks on the API level hit the sweet spot between speed of execution and coverage of application features. Compared to the two other types of automated tests in the test automation pyramid, API-level tests tend to:

  • execute faster than user interface-driven tests. User interface-driven automated tests, such as those written in Selenium WebDriver, need to fire up a browser and render several web pages every time a test is executed. When your tests go through a lot of different pages, execution time skyrockets. API-level tests, on the other hand, have to wait for a server responding to HTTP calls only. The only client-side processing that needs to be done is parsing the response and performing validation checks, for example on specific elements in the response. This is a lot faster than sending (possibly many) HTTP requests to a web server to fetch all objects required for a web page and then waiting until your browser has finished rendering the page.
  • cover more business logic than unit tests. Yes, you should have unit tests. And they should cover as much of the internal workings of your application that make up the business logic. However, there’s only so much unit tests can do. For example, unit tests can check whether the salary of a given employee is calculated correctly in the back end. They cannot guarantee that the same salary is correctly sent out to the front end layer of your application (or to the IRS) upon request, though. For this, you will need to perform tests at a higher level in your application, and API tests are usually perfect for that.

Automated API tests tend to be more reliable
Apart from having the right mixture of speed of execution and coverage of functional aspects, API-level automated checks have another big advantage over user interface-driven automated tests: they’re usually far more reliable. User interface-driven tests constantly have to walk the wobbly rope of synchronization, ever changing (or ‘improving’) user interface designs, dynamic element identification methods, etc. API definitions and interfaces on the other hand are amongst the most stable parts of an application: they follow standardized specification formats (such as WSDL for SOAP or WADL, RAML or Swagger for REST) and once agreed upon, an API does not usually change all that much. This applies especially to outward-facing APIs. For example, Google can easily change its Gmail user interface without this impacting end users (apart from annoyance, maybe, but that’s a different story altogether), but sudden radical changes to its Gmail API would render a lot of third-party applications that have Gmail integration useless. Therefore, changes to an API will usually be a lot fewer and further between, resulting in less maintenance required for your API-level automated tests.

A sample API test in REST Assured

Further reading
I hope this blog post has given you some insight into why I think API test automation skills are a valuable asset for any tester with an interest in test automation. If you want to read more on API testing and test automation, I highly recommend the API Testing Dojo on the SoapUI website. Additionally, you can also check out my other posts on API testing here.