1950s switchboard operators

Efficient API testing: How to get started with REST Assured

In a previous post on TechBeacon, I explained why writing automated tests at the API level offers the best of two worlds: 1) It gives you increased stability and speed of test execution compared to end-to-end tests driven through the user interface, and 2) it gives you increased scope and integration coverage compared with unit tests.

There is a wide range of tools available on the market that help you in creating automated API tests. These tools fall roughly into any of these categories: Open source, commercial, and custom.

In this post, let's take a deeper dive into API-level test automation to show you how to realize this within your own projects.  My tool of choice for this post: REST Assured, an open source Java-based Domain-Specific Language (DSL) that allows you to write powerful, readable, and maintainable automated tests for your RESTful APIs. (REST Assured does not handle SOAP-based APIs.)

Continuous testing: A practical guide

The benefits of REST Assured

There are three main aspects of REST Assured that make it a powerful library for API testing:

  • It removes the need for writing a lot of boilerplate code required to set up an HTTP connection, send a request and receive and parse a response
  • It supports a Given/When/Then test notation, which instantly makes your tests human readable
  • Since REST Assured is a Java library, integrating it into a continuous integration / continuous delivery setup is a breeze, especially when combined with a Java testing framework such as JUnit or TestNG

To illustrate the benefits of REST Assured, let's look at a simple REST Assured test. This test invokes a GET call to an API that, based on a zip code and a country code, provides some data on the location corresponding to that zip code. So, an example of the behavior of this API would look like this:

  • Given a specific country (in this case the United States) and zip code (in this case 90210)
  • When we perform a GET call to the RESTful API
  • Then the response returned indicates that the 90210 zip code corresponds to Beverly Hills

Translated to a REST Assured test, this looks as follows:

@Test
public void checkCityForZipCode() {
        
  given().
    pathParam("country","us").
    pathParam("zipcode","90210").
  when().
    get("http://api.zippopotam.us/{country}/{zipcode}").
  then().
    assertThat().
    body("places.'place name'[0]",equalTo("Beverly Hills"));
}
Note how the given, when, and then parts of the specification are directly translated to an executable REST Assured test. If you want an explanation for how exactly to deal with path and query parameters, and how to extract specific elements from a JSON or XML response as returned by an API, you should refer to the REST Assured usage guide.

Also note that the expressive power offered by REST Assured is demonstrated here through how the complete test (setup, execution, and assertion) is written using a single line of code.

Other REST Assured test writing features

REST Assured offers a number of other useful features for writing tests for RESTful APIs. Let's take a look at some of them.

Technical response data validation

In the example above, I verified that a particular element in the response body (the city name) was equal to a predefined expected value ('Beverly Hills'). Checking that an API works as intended, however, does not end with checking response contents. For the application or component receiving the response, it is also of vital importance that the response is technically sound: e.g., That the response has the right status code, the required header values and that it is formatted correctly.

These criteria can be checked using REST Assured. For example, if you want to check that the response for the previously executed API call has a status code of 200 (indicating that all is OK) and that the response header indicates that the body is to be interpreted as JSON, you can do as follows:

@Test
public void checkHeaderData() {
        
  given().
    pathParam("country","us").
    pathParam("zipcode","90210").
  when().
    get("http://api.zippopotam.us/{country}/{zipcode}").
  then().
    assertThat().
    statusCode(200).
  and().
    contentType(ContentType.JSON);
}

As you can see, with REST Assured, verifying technical response data is just as straightforward as checking response body contents.

Data-driven testing

Another feature of REST Assured that makes your API testing more powerful is the ability to create data-driven tests. Let's say you want to extend the zip code test created earlier with a second test case: you also want to check that zip code 12345 is associated with the city of Schenectady, New York. However, instead of simply copying the entire test and replacing the relevant parts, you would like to feed a single test with two different data records (a concept known as data-driven testing), because this reduces any potential maintenance efforts, while also making it easy to add even more tests.

To achieve this, you first need to create a test data set. In this example, I am going to use the DataProvider object that is offered by TestNG to do this:

@DataProvider(name = "zipcodes")
public String[][] createZipCodeTestData() {
        
  return new String[][] {
    {"us","90210","Beverly Hills"},
    {"us","12345","Schenectady"}
  };
}

Next, you create a data-driven REST Assured test that's associated with this test data set:

@Test(dataProvider = "zipcodes")
public void aZipCodeTest(String country, String zipcode, String city) {
        
  given().
    pathParam("country",country).
    pathParam("zipcode",zipcode).
  when().
    get("http://api.zippopotam.us/{country}/{zipcode}").
  then().
    assertThat().
    body("places.'place name'[0]",equalTo(city));
}
When you run this test, it'll be executed twice, once for each record in the test data set.

As you can see, creating data-driven tests in REST Assured is a very straightforward "next step" when you've already got tests in place for a single test data record. Note that I used TestNG to create the test data object and to associate it with the actual test, but you can easily do something similar if you're using JUnit.

Support for authentication mechanisms

A lot of RESTful APIs require a consumer to authenticate itself before it is allowed to interact with it. REST Assured supports a number of commonly used API authentication mechanisms, including Basic (username and a password in the header of every call) and OAuth 2.0 authentication. For example, here's how you can call an API that's secured using Basic authentication with REST Assured. 

@Test
public void checkBasicAuthentication() {

  given().
    auth().
    preemptive().
    basic("username","password").
  when().
    get("http://mysecureapi.com/basicauth").
  then().
    assertThat().
    body("authentication_result",equalTo("Success"));
}

Learn more REST Assured features

Apart from those covered in this post, REST Assured offers a wide range of other useful features that can create powerful automated tests for your RESTful APIs. You can read more about those in the usage guide.

If you're looking for more examples and some exercises to practice with REST Assured, I have published a workshop on REST Assured, which I have delivered several times, on my own GitHub page.

Continuous testing: A practical guide
Topics: Quality