A Java Test Automation Framework for API Testing

In this article, Oleksandr Podoliako shares with us some insights of experience in writing a test automation framework to test API with Java.

Author: Oleksandr Podoliako

Recently, I have received a test task to write a test automation framework for API testing with Java. I think it could be interesting for others and I would like to share the results.

The framework was written with Java. Maven, Lombok, testNG and Rest Assured were also used. The framework consists of clients and test layers. If the tests are planned to be E2E or the business logic is complicated, then a business layer, which contains a few API clients, could be added. Also, you could also add a Cucumber layer.

Test Automation Framework for API Testing With Java

The main idea is to generalize the base API client, which contains main basic API methods (GET, POST, PUT and DELETE). This gives us an opportunity to use one client to interaction with almost all endpoints. Also, it allows configuring the Rest Assured log in one place through baseAPIClient.

public class BaseAPIClient {
   //some code was cut

   public <T, K> ResponseWrapper<T> getEntity(Class<T> t, RequestWrapper<K> requestWrapper, String url) {
       ResponseWrapper<T> responseWrapper = new ResponseWrapper<>();
       Response response = configureRequest(requestWrapper)
               .when()
               .get(url);

       responseWrapper.setBody(response.as(t));
       responseWrapper.setResponseRaw(response);

       configureResponse(responseWrapper);

       return responseWrapper;
   }

   //some code was cut

   private <T> RequestSpecification configureRequest(RequestWrapper<T> requestWrapper) {
       RequestSpecification requestSpecification = RestAssured.given();

       switch (logRequest()) {
           case "all":
               requestSpecification.log().all();
           case "parameters":
               requestSpecification.log().parameters();
           default:
               requestSpecification.log().method();
       }


       if (requestWrapper.getHeaders() != null) {
           for (String key : requestWrapper.getHeaders().keySet()) {
               requestSpecification.header(key, requestWrapper.getHeaders().get(key));
           }
       }

       if (requestWrapper.getQueryParameters() != null) {
           for (String key : requestWrapper.getQueryParameters().keySet()) {
               requestSpecification.queryParam(key, requestWrapper.getQueryParameters().get(key));
           }
       }

       return requestSpecification;
   }

   //some code was cut
}

Two wrappers, which were generalized, were created to have a unified approach. RequestWrapper and ResponseWrapper get their types in tests. Also, they contain raw data from the Rest Assured.

@Getter
@Setter
@Builder
public class RequestWrapper<T> {

   private Map<String, String> headers;
   private Map<String, String> queryParameters;

   private T body;
}
@Getter
@Setter
public class ResponseWrapper<T> {
   private T body;
   private Response responseRaw;

   public int getStatusCode() {
       return responseRaw.getStatusCode();
   }
}

BaseAPIClient can be used to interact with different endpoints. However, I recommend creating a separate client for every endpoint, which inherits from BaseAPIClient main functionality. Separate API clients could be expanded with specific API methods like search and filter. Base API method from BaseAPIClient can be overridden.

public class PostsClient extends BaseAPIClient{
}

I support the ideas of isolation and managing state of application. You can read more about this in my previous article (Automated Testing Principles). I encourage you to create and remove test data in before and after methods. It will increase tests reliability.

@BeforeMethod
public void init() {
   postsClient = new PostsClient();
   softAsserts = new SoftAssert();

   Post post = Post.builder()
           .userId(1)
           .title("title")
           .body("body")
           .build();

   RequestWrapper<Post> requestWrapper = RequestWrapper.<Post>builder()
           .headers(HEADERS)
           .body(post)
           .build();

   responseWrapperPreconditions = postsClient.postEntity(Post.class, requestWrapper, URL);
}
@AfterMethod
public void cleanup() {
   RequestWrapper<Post> requestWrapper = RequestWrapper.<Post>builder()
           .headers(HEADERS)
           .body(responseWrapperPreconditions.getBody())
           .build();

   postsClient.deleteEntity(Post.class, requestWrapper
           , URL + responseWrapperPreconditions.getBody().getId());
}

I recommend writing test, which cover one REST method, in one class because the preconditions are complicated and can be different for every REST method. Also, I recommend grouping a few asserts in one test method because of optimization. The more atomic approach is valid.

@Test
public void testGetPosts() {
   RequestWrapper<Post> requestWrapper = RequestWrapper.<Post>builder()
           .headers(HEADERS)
           .build();


   ResponseWrapper<Post> responseWrapper = postsClient.getEntity(Post.class, requestWrapper
           , URL + responseWrapperPreconditions.getBody().getId());

   softAsserts.assertEquals(responseWrapper.getStatusCode(), 200
           , "Response status code should be 200");

   softAsserts.assertTrue((isJsonSchemaValid("postsGetSchema.json",
                   convertToStringJSON(responseWrapper.getBody())))
           , "The search returns response with invalid json schema");

   softAsserts.assertEquals(responseWrapper.getBody().getId(), responseWrapperPreconditions.getBody().getId()
           , "Response id should be correct");
   softAsserts.assertEquals(responseWrapper.getBody().getUserId(), responseWrapperPreconditions.getBody().getUserId()
           , "Response userId should be correct");
   softAsserts.assertEquals(responseWrapper.getBody().getTitle(), responseWrapperPreconditions.getBody().getTitle()
           , "Response title should be correct");
   softAsserts.assertEquals(responseWrapper.getBody().getBody(), responseWrapperPreconditions.getBody().getBody()
           , "Response body should be correct");
   softAsserts.assertAll();
}

This test has a presentation goal and is being failed because the testing API application does not actually create and remove entities.

The full code can be found on GitLab

About the Author

Oleksandr Podoliako has been working in different software projects as a test automation engineer since 2017. His main technology stack is the Java stack. He has passed more than 5 IT certifications. He has written a few testing related topics and has conducted more than 30 interviews in different languages with candidates from all over the world.

This article was originally published on https://oleksandr-podoliako.medium.com/test-automation-framework-for-api-testing-with-java-ffd80259fe87 and is re-published here with permission.

Be the first to comment

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.