Writing integration tests for API's is challenging in a micro-services world as it involves multiple API's from multiple components.
In this article we will be focusing on the two major challenges of writing API Integration Testing. It also talks about how to overcome them by using modern testing tools and techniques. We will be using PactumJS to write automated API integration test cases.
We use Github Discussions to receive feedback, discuss ideas & answer questions.
Installation
# install pactum as a dev dependency
npm install --save-dev pactum
# install a test runner to run pactum tests# mocha / jest / cucumber
npm install --save-dev mocha
These are the two things of many that I personally felt challenging while writing API Integration Tests.
Passing data across tests.
Retry on failed expectations.
Example
It's always better to have an example to understand the core concepts of a topic under discussion.
Let's take a simple example of an e-commerce application with the following API endpoints for processing an order.
POST/api/orders (for placing an order)
POST/api/payments (for making a payment)
GET/api/payments/{payment_id} (for fetching the status of payment)
Workflow
To make things clear, the requests and responses shown below are overly simplified.
Step 1 - Place Order
A user comes in and makes a POST request to /api/orders with the following payload to place an order.
Request Payload
{"product":"PlayStation 5"}
At the time of writing this article, it's highly impossible to buy the above product. At-least in some places. 🙂
Now the server responds with the following response body which contains the order id.
Response
{"id":"1f4c99e9-12df-45d4-b455-98418f4e3b1e"}
This order id is dynamically generated by the API server. We need to grab it and pass it to the other endpoints like payments to complete the order.
Using any testing library, we can save the response in a variable and use them later. It works but not efficient. Because when we write integration tests for large scale applications, it forces us to pass significant amount of data between tests and API calls. Declaring intermediary variables will damage the readability of the code.
To overcome this challenge PactumJS comes with a concept of Data Store to pass data between API calls across tests.
The above test will make a POST request to /api/orders with given json payload and once the response is received it expects the status should be 200 and stores the value of id into a special variable called OrderID which is internal to PactumJS.
Step 2 - Make Payment
The next step is to make the payment. Now the user makes a POST request to /api/payments with the following payload.
PactumJS will internally replace $S{OrderID} with 1f4c99e9-12df-45d4-b455-98418f4e3b1e before making the request.
In the above test case we are also saving the payment id into the special variable PaymentId using the stores method. Using the payment id we can track the status of the payment. So this brings us to the final step of our integration test.
Step 3 - Wait for Payment to be completed.
To get the status of the payment, user makes a GET request to the /api/payments/{payment_id} endpoint.
The API responds with the following response body.
Response
{"status":"in-progress"}
As you see, the status is still in-progress. We need to wait for few seconds for the payment to be completed.
Including hard waits is a bad practice in testing. PactumJS comes with a concept of retry mechanism which retries on failed expectations. It is similar to fluent wait in selenium.