Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Test automation of REST APIs Using ZeroCode Open Source JSON-Based Test Framework

DZone's Guide to

Test automation of REST APIs Using ZeroCode Open Source JSON-Based Test Framework

See how the ZeroCode testing library can make your life easier by reducing boilerplate code, automating your tests, and more.

· DevOps Zone ·
Free Resource

Learn more about how CareerBuilder was able to resolve customer issues 5x faster by using Scalyr, the fastest log management tool on the market. 

 => Browse the demo HelloWorld ZeroCode maven project

 => Download(zip) the demo HelloWorld ZeroCode project - (24kb)

 => Browse the demo Consumer-Contract-Testing project using Zerocode lib

 => Download(zip) the demo Consumer-Contract-Testing maven project - (28kb)

This post will explain how we can design better test cases whether it is BDD or TDD. Many folks tend not to write tests because sometimes it is harder to think up a use case scenario and put into actual executable test.

Testing need not be harder or slower, it should be easier and faster.

See the sample contract tests for Corp Bank Application above as an example(you can clone and execute locally as well). 

Zerocode a simple and light weight testing library brings the simplicity in testing and validating APIs by eliminating repetitive code for test assertions, http calls and payload parsing. See an example how. It's powerful JSON comparison and assertions make the testing cycle a lot easy and clean. It empowers automation test engineers and developers to build up test scenario steps effortlessly using declarative JSON steps. See Hello- GitHub API Test Step and HelloWorldTest, the README file. 

The test cases are simple and looks like below diagram, the rest of the job like invoking the end point using HttpClient, receiving the response and asserting against the "assertions" block is handled by the framework.

Your Given-When-Then User-Journey Scenario/AC,

AC1:
GIVEN- the POST api end point '/api/v1/users' to create an user,     
WHEN- I invoke the API,     
THEN- I will receive the 201 status with the new user ID and headers 
AND- I will assert the response

AC2:
GIVEN- the REST api GET end point '/api/v1/users/${created-User-Id}',     
WHEN- I invoke the API,     
THEN- I will receive the 200 status with body and headers
AND- I will assert the response

precisely translates to  the below JSON steps(simple and clean)-

Image title

The Pass and Fail cases shows how easily a test case can be represented n read- pass_fail_combined


Why?

  • Zerocode helps you to focus on test scenarios and ACs(Acceptance Criteria) , without any distractions of unnecessary syntax overhead.

  • Enables you to automate your API Contract tests, end-to-end  integration-tests at the speed of writing unit tests.

Displays step's all assertions failures at once, than exiting at first failure.

e.g. you have a  REST api End point  to test, with the following behavior:

Usecase scenario: REST API to create, update an empoyee details,
---------------------- POST -----------------------
URL: http://host:port/api/v1/persons,
Operation: POST,
Request body:
{
   "name": "Larry P",
   "job": "Full Time"
},
Expected JSON Response body as-
{
   "id": 1001
},
Expected Response status: 201

---------------------- PUT -----------------------
URL: http://host:port/api/v1/persons/1001,
Operation: PUT,
Request body:
{
    "id": 1001
    "name": "Larry Page",
    "job": "Co-Founder"
},
Expected JSON Response body as-
{
    "id": 1001
    "name": "Larry Page",
    "job": "Co-Founder"
},
Expected Response status: 200

---------------------- GET -----------------------
URL: http://host:port/api/v1/persons/1001,
Operation: GET,
Expected JSON Response body as-
{
    "id": 1001
    "name": "Larry Page",
    "job": "Co-Founder"
},
Expected Response status: 200

And your happy scenario test case code  with assertions, exactly looks like below:

{
      "name": "create_emp",
      "url": "http://host:port/api/v1/persons",
      "operation": "POST",
      "request": {
             "name": "Larry P",
             "job": "Full Time"
       },
      "assertions": {
          "status": 201,
          "body": {
             "id": 1001
          }
      }
}

Your negative scenario test case code with assertions, exactly looks like below:

{
      "name": "get_not_existing_emp_details",
      "url": "http://host:port/api/v1/persons/9999",
      "operation": "GET",
      "request": {},
      "assertions": {
          "status": 404,
          "body": {
             "message": "No such employee exists"
          }
      }
}

And optionally-

if you need them together as scenario stepswith assertions, then the code exactly looks like below:

{
    "scenarioName": "Create, Update and GET Employee Details Happy and Sad test",
    "steps": [
      {
          "name": "new_emp",
          "url": "http://host:port/api/v1/persons",
          "operation": "POST",
          "request": {
                 "name": "Larry P",
                 "job": "Full Time"
           },
          "assertions": {
              "status": 201,
              "body": {
                 "id": "1001"
                }
            }
      },
      {
          "name": "update_emp",
          "url": "http://host:port/api/v1/persons/${$.new_emp.response.id}",
          "operation": "PUT",
          "request": {
                 "name": "Larry Page",
                 "job": "Co-Founder"
           },
          "assertions": {
              "status": 200,
              "body": {
                 "id": "${$.new_emp.response.id}"   //This is optional but better than hardcoding ${JSON Path to created Id}
                 "name": "${$.update_emp.request.name}",
                 "job": "${$.update_emp.request.job}"
               }
            }
        },
        {
            "name": "get_emp_details",
            "url": "http://host:port/api/v1/persons/${$.new_emp.response.id}",
            "operation": "GET",
            "request": {},
            "assertions": {
                "status": 200,
                "body": {
                   "id": "${$.new_emp.response.id}"
                   "name": "${$.update_emp.request.name}",
                   "job": "${$.update_emp.request.job}"
                }
            }
        },
        {
            "name": "get_non_existing_emp_details",
            "url": "http://host:port/api/v1/persons/9999",
            "operation": "GET",
            "request": {},
            "assertions": {
                "status": 404,
                "body": {
                    "message": "No such employee exists"
                }
            }
        }
    ]
}

Then you just stick these into a JSON file, for example, named "get_happy_and_sad.json"anywhere in the test/resources  folder. Then run the code like below, pointing to that JSON fileand then you are done with testing. It's neat and as simple as running a JUnit @Test.

@RunWith(ZeroCodeUnitRunner.class)
@HostProperties(host="http://localhost", port=8088, context = "")
public class MyRestApiTest{

    @Test
    @JsonTestCase("get_happy_and_sad.json")
    public void testGetHappyAndSad() throws Exception {
    }

}

How?

<dependency>
    <groupId>org.jsmart</groupId>
    <artifactId>zerocode-rest-bdd</artifactId>
    <version>1.2.x</version>
</dependency>
  • Hello World and samples are available to download or clone.
    • You can organize and arrange the tests to suit your requirements, by folder/feature/release.
    • You can add as many tests as you want by just annotating the test method. See here(many scenarios) for as examples. The below example is  here and can be executed via JUnit from here(using @Test). Unit test looks like below- 

        @Test
        @JsonTestCase("helloworld_more/hello_world_json_tree.json")
        public void testMoreDepthJson_asItIs() throws Exception {
        }
  • IDE screen shot of samples
    • You can assert the entire JSON in the assertion block, however complex and hierarchical the structure might be, with a copy paste of the entire JSON. Hassle free, no serialize/deserialize as needed!

    • You can also use only the particular section or even an element of a JSON using a JSON path like  $.get_emp_details.response.body.id, which will resolve to 1001 in the above case.

    • You can test the consumer contract APIs by creating runners specific to clients.

  • Test Report

    Test reports are generated into the /target folder every time the tests are run. Sample reports are here as Interactive and Searchable(filter) HTML .html Spike Chart and .csv tabular format. See more about reports here.

    Test Logs

    Test logs are generated in the console as well as into the log file in a readable JSON format,  target/logs/zerocode_rest_bdd_logs.log. In case of a test failure, it lists which field or fields didn't match with their JSON Pathin a tree structure.

    For example, if the test passedTest Passed.

    If the test failedTest Failed.

    If there are more fields mismatches i.e. "assertions" block doesnt match with "actual" response, then the output displays like below-

    (You can run yourself the below step via the unit test from the hello-world repo )

    all_assertion_failures_at_once


    Examples

    Source Code in GitHub

    Visit the source here in GitHub ZeroCode.

    Running tests parallelly

    Tests can be run parallely both ways, Class wise or Method wise.

    Contribute

    Raise issues and contribute to improve the ZeroCode library and add more essential features you need by talking to the author.

    For quick, concise and short answers on questions you can optionally put your queries in gitter.

    Find out more about how Scalyr built a proprietary database that does not use text indexing for their log management tool.

    Topics:
    rest api ,api testing ,test automation ,bdd

    Opinions expressed by DZone contributors are their own.

    {{ parent.title || parent.header.title}}

    {{ parent.tldr }}

    {{ parent.urlSource.name }}