

API Integration Testing Made Easy
source link: https://dev.to/asaianudeep/api-integration-testing-made-easy-1lcp
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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.
PactumJS
REST API Testing Tool for all levels in a Test Pyramid
PactumJS is a REST API Testing Tool used to automate e2e, integration, contract & component (or service level) tests.
- ⚡ Swift
- 🎈 Lightweight
- 🚀 Simple & Powerful
- 🛠️ Compelling Mock Server
- 💎 Elegant Data Management
- 🔧 Extendable & Customizable
- 📚 Clear & Comprehensive Testing Style
- 🔗 Component, Contract & E2E testing of APIs
Documentation
This readme offers an basic introduction to the library. Head over to the full documentation at https://pactumjs.github.io
Need Help
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
Usage
pactum can be used for all levels…
Challenges
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.
Let's look at the test first.
await pactum.spec()
.post('/api/orders')
.withJson({
"product": "PlayStation 5"
})
.expectStatus(200)
.stores('OrderID', 'id');
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.
Request Payload
{
"order_id": "1f4c99e9-12df-45d4-b455-98418f4e3b1e",
"card_info": {
"number": "1111-1111-1111-1111",
"expiry": "11/11",
"cvv": "111"
}
}
Now the API responds with the following response body which contains payment id
.
Response
{
"id": "a32fce50-d4e8-4d95-b16f-57fd13fbb7df"
}
Now let's talk about the test case.
As you observed, the order id
from the previous request is included in the request payload.
To get the value of special internal variable, PactumJS uses a special pattern - $S{<variable-name>}
to access it.
Let's look at the test.
await pactum.spec()
.post('/api/payments')
.withJson({
"order_id": "$S{OrderID}",
"card_info": {
"number": "1111-1111-1111-1111",
"expiry": "11/11",
"cvv": "111"
}
})
.expectStatus(200)
.stores('PaymentID', 'id');
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.
Let's look at the test case.
await pactum.spec()
.get('/api/payments/{id}')
.withPathParams('id', '$S{PaymentID}')
.expectStatus(200)
.expectJson({
"status": "completed"
})
.retry();
By default it retries 3
times with a delay of 1000ms
between each retry.
Complete Test
Now let's take a look at the entire test using PactumJS and mocha.
const pactum = require('pactum');
it('order PS5 and make payment', async () => {
await pactum.spec()
.post('/api/orders')
.withJson({
"product": "PlayStation 5"
})
.expectStatus(200)
.stores('OrderID', 'id');
await pactum.spec()
.post('/api/payments')
.withJson({
"order_id": "$S{OrderID}",
"card_info": {
"number": "1111-1111-1111-1111",
"expiry": "11/11",
"cvv": "111"
}
})
.expectStatus(200)
.stores('PaymentID', 'id');
await pactum.spec()
.get('/api/payments/{id}')
.withPathParams('id', '$S{PaymentID}')
.expectStatus(200)
.expectJson({
"status": "completed"
})
.retry();
});
Conclusion
Writing readable and maintainable tests is very important to make API testing productive and enjoyable experience.
PactumJS abstracts the challenging parts to write tests in an easy and fun way and ultimately making API Integration testing super easy.
Recommend
-
10
Picasso: Component Library Testing Made EasyA new version of Toptal’s design system was released recently which required that we make changes to almost every component in Picasso, our in-house compone...
-
12
In this post, we will take a look at how we can use Testcontainers for our integration tests. Testcontainers will allow us to write integration tests making use of containerized databases, message queues, web browsers, etc. without a dependen...
-
13
Alba v5.0 is out! Easy recipes for integration testing ASP.Net web services Leave a...
-
7
Easy Jakarta EE Integration Testing with the Payara Platform and Testcontainers by Fabio Turizo Originally published on Payara.Fish. Payara provides J...
-
10
What’s the problem? Testing data migrations is one of the cases where investing in good testing is crucial. Unfortunatly it’s hard to cover all the scenarios and it can be very time consuming to write test code for all of the scena...
-
15
Made Easy With SQL Developer Nowadays, everything is about automation. Software testing is no exception. After an introduction, we will create and run utPLSQL tests with Oracle SQL Developer for a fictitious user story. Our te...
-
8
Automated Testing React Components Made Easy With This Secret
-
7
Node Featured Unit Testing Made Easy with NestJS...
-
11
Monitoring Made Easy: How to Use Micrometer API 28 April 2023 by F.Marchioni
-
9
Toushif Ahmed May 16, 2023 5 minute read ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK