API Testing with Java and Spring Boot Test - Part 2: Improving the solution
Tags: java, restassured, beginners, tutorial
Published: 2023-02-07 08:06:43

In the last part of this step-by-step, we created the project, set up the test framework and also did all the configurations needed to run our API tests.

You can see the first part of the series here:
- [API Testing with Java and Spring Boot Test - Part 1: The basic setup](https://dev.to/luuizeduardo/api-testing-with-java-and-spring-boot-test-part-1-the-basic-setup-5db0).

Let's continue to grow our test framework, but first, we need to do some improvements to the existing code. In this guide, we'll:

- Refactor the object mapping (to be easier to handle with the JSON files)
- Improve the response validations  
- Handle multiple environments inside our tests.

These changes will let our code base cleaner and easier to maintain while helping us to create a scalable framework of API tests.

Let's do it.

![Homer Simpson woo hoo](https://media.tenor.com/4i_vCC1wVw4AAAAC/homer-simpson-woohoo.gif)

## Refactoring the Object mapping

We'll take advantage of using the Spring boot `Repository` to separate the responsibility of mapping the objects (JSON) we're going to use inside our tests. That way, we can do another step forward in our code cleanup.

So, first of all, we're going to:

- Create a new package called `repositories` 
- Then we create a new `Class` inside this package called `FileUtils`.

We'll also take the opportunity to change the way we map the object to not be hard-coded but be in a proper resource file. That way when we need to change the test data, we don't have to change the test but only the correspondent resource file.

{% embed https://gist.github.com/luuizeduardo/2407a2969cec12485ed69574cf5a7fee.js %}

As you can see in the file above, we created a function to read a JSON file and then return the object already mapped - similar to the approach we had before in the test file.

Now, we'll structure the `resources` folder to accommodate the JSON files.

In the `resources` folder, let's create a new directory called `user` and then create a file to store the request body of the operation we'll do.

{% embed https://gist.github.com/luuizeduardo/640d283601928a87471a193800e0b3fe.js %}

After that, we need to update our test. Now we want to get the file data by using the new function we created for that purpose. 

The updated test will look like that:

{% embed https://gist.github.com/luuizeduardo/48d88925dd9541913f4c5aace81d620c.js %}

Much better! By keeping the code cleaner, we are helping our future selves with its maintenance - trust me, you'll be very glad to see this.

## Improving the response validation

Great! Now, let's have a look at the response validation. 

In some cases, we want to check the full response body - or at least some parts of it - to fulfill the test requirements.

To do that, we'll create:

- A new `Repository` to abstract the responsibility and help us check the full JSON response body
- A function to handle the check of the JSON response.

We'll also add the "jsonassert" dependency to assert the JSON.

The `pom.xml` file will look like that:

{% embed https://gist.github.com/luuizeduardo/62c8f0a33095016fc1832f9c6045ae88.js %}

The newly created `ResponseUtils` class will be something like this:

{% embed https://gist.github.com/luuizeduardo/a560af2734340b3cfa6befbff05c57a0.js %}

The next step should be to use this new function and improve our test. To do that, we'll configure a GET request on `YourApiService` and return the full `Response` object. Then we should be able to check the response body.

```java
public Response getRequest(String endpoint) {

    return RestAssured.given(spec)
        .contentType(ContentType.JSON)
    .when()
        .get(endpoint);
}
```

Now, it's just a matter of adding the test case to the `ApiTest` test class and using the same strategy of letting the JSON response file be in its proper directory. Finally, we'll have something like this:

```java
@Test
public void testGetUser() throws IOException, JSONException {

    Response res = yourApiService.getRequest("/users/2");

    JsonNode expectedResponse = FileUtils.readJsonFromFile("responses/user/specific.json");
    
    assertThat(res.statusCode(), is(equalTo(200)));
    ResponseUtils.assertJson(res, expectedResponse);
}
```

Quite easy to understand if you just look at the test case :)

![Homer Simpson relieved](https://media.tenor.com/TRjZAIXAxL8AAAAC/donut-simpson.gif)

## Executing the tests over multiple environments

Now we have the tests properly set, and everything is in the right place. One thing that could be in your mind right now is: "Ok, but I have a scenario in my product in which I need to run my test suit over multiple environments. How do I do that?". 

And the answer is - property files.

The property files are used to store specific data which we can use along our test suit, like the application host, port, and path to the API. You can also store environment variables to use within your test framework. However, be careful since we don't want to make this information public. You can see an example in the lines below.

With Spring boot, we take advantage of using the "profiles" to set the specifics of the environments our application has, and make them available as Spring boot profiles.

So, let's do that. Inside the `resources` folder, we'll create a new file called `application-prod.properties` to store the values of the production cluster of the test application. The file will store something like this:

```java
apitest.base.uri=https://reqres.in
apitest.base.path=/api
apitest.token=${TOKEN}
```

Now, the only thing missing is to change our service to get the values stored in the property file. 

To get the values from the property files, we'll use the annotation `@Value`. This annotation will provide the values from the properties we set in the `application-prod.properties` file.

**Bear in mind: ** You'll need to set the environment variable before using it here. The `@Value` annotation will grab this value from the environment variables you have set.

The updated version of `YourApiService` class will look like this:

{% embed https://gist.github.com/luuizeduardo/570b95bf8f9c7791073c01d9cd123e24.js %}

That's a great step up. This way, if you have multiple environments in your setup, you just need to create another `application-YOUR_PROFILE_NAME.properties`.

## Executing the test suit

You must be wondering: How do I run the test suit with this newly created profile? 

The answer is simple, just execute `mvn clean test -Dspring.profiles.active=prod`.

By default, if you just run the `mvn clean test` command, Spring Boot will try to find a file called `application.properties` and automatically activate it.

Now we have significantly improved the test setup of our application by: 
- The refactoring of the Object mapping to clean up our code and apply some best practices
- Improving the response validation by adding a new dependency and using it to simplify the check
- Learning how to handle multiple test environments. This should be useful when it comes to companies that have layers of environments before the code reach production

Are you curious about the article? Building a Java API test framework part 3 will further improve our application. We will then go deeper into the following topics:
- Test reporting with Allure reports
- Configure a CI pipeline with GitHub actions
- Publish the test report on GitHub pages

See you soon!