API testing with Java and REST Assured (and The Beatles)

Dennis Whalen - Nov 15 '20 - - Dev Community

REST Assured is an open source Java library that is used when building automated tests for REST endpoints. In my previous REST Assured post I covered how to get your environment setup and how to run a couple basic REST Assured tests.

In this post I want to cover some more features of REST Assured and provide examples of ways you might want to use it in your automated API testing.

As a bonus, you might learn something about albums from The Beatles!

Setting up JSON Server

Im my previous article we used publicly available REST endpoints for our tests. For this article I want build tests for endpoints that I create and define. To do this I am going to quickly setup my own endpoints locally, using an open source tool called JSON server.

To get that setup on your machine:

  • Install json-server locally with npm install -g json-server
  • Create a file named music-db.json and paste the following into it:
{
  "albums": [
    {
      "id": 1,
      "artist": "The Beatles",
      "title": "Please Please Me",
      "year": "1963"
    },
    {
      "id": 2,
      "artist": "The Beatles",
      "title": "With the Beatles",
      "year": "1963"
    },
    {
      "id": 3,
      "artist": "The Beatles",
      "title": "A Hard Day's Night",
      "year": "1964"
    },
    {
      "id": 4,
      "artist": "The Beatles",
      "title": "Beatles for Sale",
      "year": "1964"
    },
    {
      "id": 5,
      "artist": "The Beatles",
      "title": "Help!",
      "year": "1965"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode
  • From the command line json-server --watch music-db.json
  • You should now have a API endpoint at http://localhost:3000/albums, and can view that data in your browser:

Alt Text

In addition to providing an endpoint to retrieve your data, JSON Server allows you to make POST, PUT, PATCH or DELETE requests, making it an ideal solution for mocking endpoints.

With JSON Server you can start building test for API endpoints before the developer implements the endpoint. You can easily mock the expected responses and once the API is implemented, your tests can point to that implementation instead of JSON Server. Development and API test automation can happen in parallel.

Now let's create some tests!

Setting the Base URI

If you still need to setup a test project, you can take a look at my first REST Assured post.

Before we get too far, let's set the base URI for our tests. We'll set the base URI once, with the testng tag @BeforeClass:

    @BeforeClass
    public void setup() {
        RestAssured.baseURI = "http://localhost";
        RestAssured.port = 3000;
    }
Enter fullscreen mode Exit fullscreen mode

Using parameters with REST Assured

REST APIs allow you to retrieve data for a particular item using the id, and the id can be passed as a path parameter or a query string parameter.

Path parameter

In our sample data, if we just want to return data for id 2, we can use a path param like this: http://localhost:3000/albums/2

Let's create a REST Assured test for this, and verify the album title is what we expect.

Create a new test in your test class:

@Test
    public void queryParamExample() {

        String idToGet = "2";
        String expectedTitle = "With the Beatles";

        // @formatter:off
        given().
                param("id", idToGet).
        when().
                get("albums").
        then().
                assertThat().
                body("title[0]", equalToIgnoringCase(expectedTitle));
        // @formatter:on
    }
Enter fullscreen mode Exit fullscreen mode

This test is getting album id 2, and verifying the title is "With the Beatles". Go ahead and run the test. It should be green.

Of course I never trust a green test, so make a temporary change to the id or expected title and verify it fails. For example, if I change the expectedTitle to something like "With the Monkeys", I should get a failed test and an error like this:

Alt Text

Query string parameter

In addition to using the path parameter, we can use a query string parameter like this:
http://localhost:3000/albums?id=2

Of course we are not limited to just retrieving data by id. We can also retrieve data by other fields, like item title:
http://localhost:3000/albums?title=With%20the%20Beatles

Now lets create a test for that:

@Test
    public void queryParamExample() {

        Integer expectedId = 2;
        String titleToGet = "With the Beatles";

        // @formatter:off
        given().
                param("title", titleToGet).
        when().
                get("albums").
        then().
                assertThat().
                body("id[0]", equalTo(expectedId));
        // @formatter:on
    }
Enter fullscreen mode Exit fullscreen mode

This is a variation on the previous test, as we are retrieving by title and verifying the id.

Both of these tests have hardcoded test data, which is typically not ideal. Let's try another strategy.

Sharing data between API calls

In this example we'll make 2 API calls. The 1st call is just getting all of the albums and extracts the json response to a Response object.

The 2nd call is retrieving a single album by id, and then asserting that the album title matches the album title that was extracted in the first call.

@Test
    public void extractDataAndPassToNextAPICall() {

        // @formatter:off
        Response response = given().
                            when().
                                get("albums").
                            then().
                                extract().
                                    response();
        String validId = response.jsonPath().getString("id[0]");
        String validTitle = response.jsonPath().getString("title[0]");

        given().
                pathParam("id", validId).
        when().
                get("albums/{id}").
        then().
                assertThat().
                body("title", equalTo(validTitle));
        // @formatter:on
    }
Enter fullscreen mode Exit fullscreen mode

With this strategy, the first call is used to extract valid test data so we can confirm the 2nd call is returning the correct data. Since we're doing this with no hardcoded test data, changes to the underlying data will not cause the test to fail.

POSTs

In addition to testing GET endpoints, we can also test POSTs and DELETEs. Let's create a test that POSTs a new album and verifies the response.

@Test
    public void postNewAlbum() {

        Header acceptJson = new Header("Accept", "application/json");

        JSONObject requestParams = new JSONObject();
        requestParams.put("artist", "The Beatles");
        requestParams.put("title", "Rubber Soul");
        requestParams.put("year", "1965");

        // @formatter:off
        //add the new album
        Response response =
                given().
                    contentType(ContentType.JSON).
                    body(requestParams.toString()).
                when().
                    post("/albums").
                then().
                    statusCode(201).
                    body("$", hasKey("id")).
                    body("title",equalTo("Rubber Soul")).
                    body("year",equalTo("1965")).
                    extract().response();
        // @formatter:on
    }
Enter fullscreen mode Exit fullscreen mode

With this test we POST the data related to our new album. We do that by creating a new JSONObject with the artist, title, and year of the new album. This object is then added to the body in the given section of the test.

You'll need to add a dependency to your POM file to get the JSONObject reference:

        <dependency>
            <groupId>top.jfunc.common</groupId>
            <artifactId>converter</artifactId>
            <version>1.8.0</version>
        </dependency>
Enter fullscreen mode Exit fullscreen mode

In the then section we are validating the data was successfully posted. We do that by comparing the data that is returned in the response to the data we originally sent. Go ahead and run this test, it should be green. As always, change something to be sure it fails when it should.

DELETEs

Now we can create a test that validates a DELETE. Since we are already adding a new album, let's delete it once it's added:

@Test
    public void postNewAlbumThenDelete() {

        Header acceptJson = new Header("Accept", "application/json");

        JSONObject requestParams = new JSONObject();
        requestParams.put("artist", "The Beatles");
        requestParams.put("title", "A Hard Day's Night");
        requestParams.put("year", "1964");

        // @formatter:off
        //add the new album
        Response response =
                given().
                    contentType(ContentType.JSON).
                    body(requestParams.toString()).
                when().
                    post("/albums").
                then().
                    statusCode(201).
                    body("$", hasKey("id")).
                    body("title",equalTo("A Hard Day's Night")).
                    body("year",equalTo("1964")).
                    extract().response();

        //delete album that was just added
        given().
            contentType(ContentType.JSON).
            body(requestParams.toString()).
        when().
            delete("/albums/" + response.jsonPath().getInt("id")).
        then().
            statusCode(200);

        //try to get the album we just deleted
        given().
        when().
            get("/albums/" + response.jsonPath().getInt("id")).
       then().
            statusCode(404);

        // @formatter:on
    }
Enter fullscreen mode Exit fullscreen mode

With this test we have 3 API calls:

  • add the new album
  • delete the new album and verify the status code is 200
  • get the album that was just deleted and verify it does not exist

Wrap-up

With this article you should have a good start on using REST Assured for testing API endpoints. Of course there is a lot more to it than the basics I've described, so go ahead and do some more research on your own at the official REST Assured website. There are plenty of resources on the web.

And if you came to this article for the Beatles info, at this point you are likely disappointed. Good news though, there are more albums than the ones mentioned above, so check out the web!

You can find the complete code for this series of blog posts in my Github project.

In future posts I am going to look at validating validating JSON schema and reporting, so stay tuned.


Smart EDJE Image

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .