{"id":8665,"date":"2022-11-17T17:22:57","date_gmt":"2022-11-17T17:22:57","guid":{"rendered":"https:\/\/www.softwaretestingmagazine.com\/?p=8665"},"modified":"2023-01-27T15:31:54","modified_gmt":"2023-01-27T15:31:54","slug":"a-java-test-automation-framework-for-api-testing","status":"publish","type":"post","link":"https:\/\/www.softwaretestingmagazine.com\/knowledge\/a-java-test-automation-framework-for-api-testing\/","title":{"rendered":"A Java Test Automation Framework for API Testing"},"content":{"rendered":"<p>In this article, Oleksandr Podoliako shares with us some insights of experience in writing a test automation framework to test API with Java.<!--more--><\/p>\n<p><strong>Author:<\/strong> Oleksandr Podoliako<\/p>\n<p>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.<\/p>\n<p>The framework was written with Java. <a href=\"https:\/\/maven.apache.org\/\">Maven<\/a>, <a href=\"https:\/\/projectlombok.org\/\">Lombok<\/a>, <a href=\"https:\/\/testng.org\/\">testNG<\/a> and <a href=\"https:\/\/rest-assured.io\/\">Rest Assured<\/a> 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.<\/p>\n<p><a href=\"https:\/\/www.softwaretestingmagazine.com\/wp-content\/uploads\/apijava2.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.softwaretestingmagazine.com\/wp-content\/uploads\/apijava2.jpg\" alt=\"Test Automation Framework for API Testing With Java\" width=\"512\" height=\"189\" class=\"alignnone size-full wp-image-8670\" srcset=\"https:\/\/www.softwaretestingmagazine.com\/wp-content\/uploads\/apijava2.jpg 512w, https:\/\/www.softwaretestingmagazine.com\/wp-content\/uploads\/apijava2-300x111.jpg 300w, https:\/\/www.softwaretestingmagazine.com\/wp-content\/uploads\/apijava2-150x55.jpg 150w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" \/><\/a><\/p>\n<p>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.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class BaseAPIClient {\r\n   \/\/some code was cut\r\n\r\n   public &lt;T, K&gt; ResponseWrapper&lt;T&gt; getEntity(Class&lt;T&gt; t, RequestWrapper&lt;K&gt; requestWrapper, String url) {\r\n       ResponseWrapper&lt;T&gt; responseWrapper = new ResponseWrapper&lt;&gt;();\r\n       Response response = configureRequest(requestWrapper)\r\n               .when()\r\n               .get(url);\r\n\r\n       responseWrapper.setBody(response.as(t));\r\n       responseWrapper.setResponseRaw(response);\r\n\r\n       configureResponse(responseWrapper);\r\n\r\n       return responseWrapper;\r\n   }\r\n\r\n   \/\/some code was cut\r\n\r\n   private &lt;T&gt; RequestSpecification configureRequest(RequestWrapper&lt;T&gt; requestWrapper) {\r\n       RequestSpecification requestSpecification = RestAssured.given();\r\n\r\n       switch (logRequest()) {\r\n           case &quot;all&quot;:\r\n               requestSpecification.log().all();\r\n           case &quot;parameters&quot;:\r\n               requestSpecification.log().parameters();\r\n           default:\r\n               requestSpecification.log().method();\r\n       }\r\n\r\n\r\n       if (requestWrapper.getHeaders() != null) {\r\n           for (String key : requestWrapper.getHeaders().keySet()) {\r\n               requestSpecification.header(key, requestWrapper.getHeaders().get(key));\r\n           }\r\n       }\r\n\r\n       if (requestWrapper.getQueryParameters() != null) {\r\n           for (String key : requestWrapper.getQueryParameters().keySet()) {\r\n               requestSpecification.queryParam(key, requestWrapper.getQueryParameters().get(key));\r\n           }\r\n       }\r\n\r\n       return requestSpecification;\r\n   }\r\n\r\n   \/\/some code was cut\r\n}\r\n<\/pre>\n<p>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.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Getter\r\n@Setter\r\n@Builder\r\npublic class RequestWrapper&lt;T&gt; {\r\n\r\n   private Map&lt;String, String&gt; headers;\r\n   private Map&lt;String, String&gt; queryParameters;\r\n\r\n   private T body;\r\n}\r\n<\/pre>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Getter\r\n@Setter\r\npublic class ResponseWrapper&lt;T&gt; {\r\n   private T body;\r\n   private Response responseRaw;\r\n\r\n   public int getStatusCode() {\r\n       return responseRaw.getStatusCode();\r\n   }\r\n}\r\n<\/pre>\n<p>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.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class PostsClient extends BaseAPIClient{\r\n}\r\n<\/pre>\n<p>I support the ideas of isolation and managing state of application. You can read more about this in my previous article (<a href=\"https:\/\/oleksandr-podoliako.medium.com\/automated-testing-principles-6cffc32f7305\">Automated Testing Principles<\/a>). I encourage you to create and remove test data in before and after methods. It will increase tests reliability.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@BeforeMethod\r\npublic void init() {\r\n   postsClient = new PostsClient();\r\n   softAsserts = new SoftAssert();\r\n\r\n   Post post = Post.builder()\r\n           .userId(1)\r\n           .title(&quot;title&quot;)\r\n           .body(&quot;body&quot;)\r\n           .build();\r\n\r\n   RequestWrapper&lt;Post&gt; requestWrapper = RequestWrapper.&lt;Post&gt;builder()\r\n           .headers(HEADERS)\r\n           .body(post)\r\n           .build();\r\n\r\n   responseWrapperPreconditions = postsClient.postEntity(Post.class, requestWrapper, URL);\r\n}\r\n<\/pre>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@AfterMethod\r\npublic void cleanup() {\r\n   RequestWrapper&lt;Post&gt; requestWrapper = RequestWrapper.&lt;Post&gt;builder()\r\n           .headers(HEADERS)\r\n           .body(responseWrapperPreconditions.getBody())\r\n           .build();\r\n\r\n   postsClient.deleteEntity(Post.class, requestWrapper\r\n           , URL + responseWrapperPreconditions.getBody().getId());\r\n}\r\n<\/pre>\n<p>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.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Test\r\npublic void testGetPosts() {\r\n   RequestWrapper&lt;Post&gt; requestWrapper = RequestWrapper.&lt;Post&gt;builder()\r\n           .headers(HEADERS)\r\n           .build();\r\n\r\n\r\n   ResponseWrapper&lt;Post&gt; responseWrapper = postsClient.getEntity(Post.class, requestWrapper\r\n           , URL + responseWrapperPreconditions.getBody().getId());\r\n\r\n   softAsserts.assertEquals(responseWrapper.getStatusCode(), 200\r\n           , &quot;Response status code should be 200&quot;);\r\n\r\n   softAsserts.assertTrue((isJsonSchemaValid(&quot;postsGetSchema.json&quot;,\r\n                   convertToStringJSON(responseWrapper.getBody())))\r\n           , &quot;The search returns response with invalid json schema&quot;);\r\n\r\n   softAsserts.assertEquals(responseWrapper.getBody().getId(), responseWrapperPreconditions.getBody().getId()\r\n           , &quot;Response id should be correct&quot;);\r\n   softAsserts.assertEquals(responseWrapper.getBody().getUserId(), responseWrapperPreconditions.getBody().getUserId()\r\n           , &quot;Response userId should be correct&quot;);\r\n   softAsserts.assertEquals(responseWrapper.getBody().getTitle(), responseWrapperPreconditions.getBody().getTitle()\r\n           , &quot;Response title should be correct&quot;);\r\n   softAsserts.assertEquals(responseWrapper.getBody().getBody(), responseWrapperPreconditions.getBody().getBody()\r\n           , &quot;Response body should be correct&quot;);\r\n   softAsserts.assertAll();\r\n}\r\n<\/pre>\n<p>This test has a presentation goal and is being failed because the testing API application does not actually create and remove entities.<\/p>\n<p>The full code can be found on <a href=\"https:\/\/gitlab.com\/OleksandrPodoliako\/api-tests\">GitLab<\/a><\/p>\n<p><strong>About the Author<\/strong><\/p>\n<p>Oleksandr Podoliako has been working in different software projects as a <a href=\"https:\/\/www.softwaretestingmagazine.com\/tag\/test-automation\/\">test automation<\/a> 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.<\/p>\n<p>This article was originally published on <a href=\"https:\/\/oleksandr-podoliako.medium.com\/test-automation-framework-for-api-testing-with-java-ffd80259fe87\">https:\/\/oleksandr-podoliako.medium.com\/test-automation-framework-for-api-testing-with-java-ffd80259fe87<\/a> and is re-published here with permission.<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"mh-excerpt\"><p>In this article, Oleksandr Podoliako shares with us some insights of experience in writing a test automation framework to test API with Java.<\/p>\n<\/div>","protected":false},"author":1,"featured_media":8671,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[43,3],"tags":[14,36],"class_list":["post-8665","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-articles","category-knowledge","tag-java","tag-test-automation"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/posts\/8665","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/comments?post=8665"}],"version-history":[{"count":0,"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/posts\/8665\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/media\/8671"}],"wp:attachment":[{"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/media?parent=8665"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/categories?post=8665"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.softwaretestingmagazine.com\/wp-json\/wp\/v2\/tags?post=8665"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}