API Testing with Java and REST Assured - Schema Validation
Tags: java, testing, codequality, tutorial
Published: 2020-11-23 11:52:26

In my [previous posts](https://dev.to/dwwhalen/series/9704) I've walked through getting started with API testing using REST Assured, and we've also created a variety of test methods to validate our API endpoints.

In this post I want to focus on schema validation, so let's get started!

#JSON Schema
JSON Schema is a JSON object that allows you to define the expected syntax and structure for a JSON response payload.  With JSON Schema you can describe how the JSON response should be structured, what fields are required, what data types are expected, and more.

For example, consider this JSON payload from our previous tests:
```json
{
    "id": 1,
    "artist": "The Beatles",
    "title": "Please Please Me",
    "year": "1963"
}
```
I have some specific requirements for this JSON object that I want to validate before I consider the response valid:
- the object should consist of id, artist, title and year
- all four fields are required
- the data type for `id` should be `integer`, the other fields should be `string`.

These rules can all be defined with a JSON Schema that looks like this:
```json
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "artist": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "year": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "artist",
    "title",
    "year"
  ]
}
```

This object is pretty self explanatory, encapsulating my rules related to required field and data types.  

There is a lot more you can do with JSON Schema.  The "grammar" to use when defining the schema is maintained at https://json-schema.org, with tons of detail [here](https://json-schema.org/understanding-json-schema/).

Once I put this schema into a JSON file, I can use it in an automated test to validate JSON payload returned from an API call.  Let's do that now!

#Validate Schema with an Automated Test
Hopefully you've been following along with the [previous posts](https://dev.to/dwwhalen/series/9704), which shows you how to get the test project and endpoint setup.  I'm ready to expand that project to include some schema validation.

###Add the REST Assured schema validation dependency

To get started we'll need to add another REST Assured  dependency to the pom.xml file that will allow us to validate our JSON Schema.
```xml
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>json-schema-validator</artifactId>
    <version>4.3.0</version>
    <scope>test</scope>
</dependency>
```
###Add the Jason Schema file to the project

We also need to put our JSON Schema file in our project.  I've created a file in `src/test/resources` named `album_schema.json` with the same content we discussed above:
```json
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "artist": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "year": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "artist",
    "title",
    "year"
  ]
}
```

As an aside, I generated that JSON Schema using https://www.liquid-technologies.com/online-json-to-schema-converter to infer a JSON Schema from the actual payload.

###Create the Schema Validation test
And finally, the test!  As discussed, the automated test is going to make the API call, then validate the JSON response matches the expected schema.  

Here's how we do that:
```java
@Test
    public void checkAlbumSchema() {
        // @formatter:off
        given().
        when().
            get("albums/1").
        then().
            log().ifValidationFails().
            assertThat().
            statusCode(200).
        and().
            contentType(ContentType.JSON).
      
      
    body(matchesJsonSchemaInClasspath("album_schema.json"));
        // @formatter:on
    }
```

Start your json-server endpoint and run the test.  If things are setup properly, you should have a green test!
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/c97zlnsaootuy25uufuh.png)

And as usual, I don't trust this test until I see it fail.  Let's change the endpoint so that a required field is not returned.  We can do that by updating our `music-db.json` file to remove the `title` from the first album, like this:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9odtjnt34fnrevg7cjl1.png)

Now run the test again.  This time the test should fail, with a message indicating a required field "title" is missing:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4r095pj3kekjcoit2akq.png)

The error also includes the actual result, so you can see the `([title])` is missing.  Go ahead and revert the change you made to `music-db.json` and make sure the test is green again.

#Validating response array
The previous test validated the API call that returns a single album object: http://localhost:3000/albums/1.  We have another endpoint that returns all the albums: http://localhost:3000/albums.  Let's create a test for that.

This new test will follow the exact same pattern as the previous test:
- make the API call
- validate the response JSON matches the expected JSON schema

The only difference is we'll have a different expected JSON schema.  

This time we're expected all the albums to be returned in an array, so instead of expecting an object, we're expecting an array of objects.  The JSON schema handles that requirement like this:
```json
"$schema": "http://json-schema.org/draft-04/schema#",
  "type": "array",
  "items": [
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer"
        },
        "artist": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "year": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "artist",
        "title",
        "year"
      ]
    }
  ]
}
```

The 2nd line of the schema shows we are expecting an array.

As before, save the schema in a JSON file in the project.  I named my file `albums_schema.json` and save at `src/test/resources`.

With the new schema created, the hard part in done.  Now we just need a test, which should look something like this:
```java
@Test
    public void checkAlbumsSchema() {
        // @formatter:off
        given().
        when().
            get("albums").
        then().
            log().ifValidationFails().
            assertThat().
            statusCode(200).
        and().
            contentType(ContentType.JSON).
            body(matchesJsonSchemaInClasspath("albums_schema.json"));
        // @formatter:on
    }
```

Run the test and verify it's green.  

We now have tests that validate the schema response for both of our GET endpoints!

#Wrap-up
Hopefully these examples give you some ideas of how you might use schema validation on your projects.  The real power here is the ability to define the schema requirements with a JSON file.  Once you have that, it's easy to plug the validation into a test by using REST Assured.

You can find the complete code for this series of blog posts in [my Github project](https://github.com/dwwhalen/HelloRESTAssured).

In my next post I want to cover the Allure reporting framework, so stay tuned!