In this article, we will explore how API testing can enhance the quality of the APIs we deliver and accelerate the development cycle. We will go through the key features of Postman for API testing, and I will provide other resources for further utilization.
Modern software development is API-first
The ever increasing complexity and capabilities of modern software such as Amazon or Netflix mean two things. First, you cannot have a single team or entity manage all features at once. And second, the application codebase is so big that you can’t possibly deploy and run it efficiently on a single machine. Inevitably, any complex application is set to become a collection of distributed applications.
That’s where APIs (Application Programming Interface) come into play! Essentially, an API is a defined set of rules to exchange information between two software components.Typically in a microservices architecture, each microservice exposes its own API to allow interactions with the other microservices. That’s why many consider that APIs are the building blocks of all modern software.
API-first development is a development model in which applications are conceptualized as an interconnection of internal and external services through APIs. This approach allows the application to be adopted by different parts of the business for multiple uses, through the API.
Why do you need API testing?
API testing is crucial because it ensures that the APIs meet expectations: first for functionality, but also on reliability, performance, and security. It helps identify flaws and inconsistencies early in the development cycle, reducing the cost and effort of fixing issues later. Additionally, API testing ensures that the interactions between different software components work as intended. And at last, you want to test that the API can handle the expected loads, it’s called load testing or stress testing.
Unit testing vs Integration testing vs API testing
Before diving into a concrete example, let’s clarify the distinction between unit testing, integration testing and API testing.
- Unit tests assess the smallest testable parts of an application, typically individual functions or methods.
- Integration tests check the interaction between different modules or services in your application to detect interface defects.
- API tests focus on the external interfaces and ensure that the API meets the contract expected by its consumers. They test the API's requests and responses, including error handling, status codes, and payload structure.
Let's consider a data processing application that analyzes sales data to generate reports. The application includes a DataProcessor
class that processes raw sales data, a ReportGenerator
class that creates reports based on processed data, and an API that allows users to submit sales data and retrieve generated reports.
Among others, you would want:
- a unit test to check if
DataProcessor
accurately cleans a single data entry with null input. - an integration test to verify that the
DataProcessor
andReportGenerator
classes work together as expected and produce the expected result when fed with some mock data. - an API test to check if, upon a valid request, the API returns a 200 status code and provides the correct report. That’s how you know that the retrieval works end-to-end.
Getting started with Postman
First things first you need to download Postman: available here.
Then, let’s create a dummy API.
Create a Postman collection
Let’s create a basic Postman collection for the routes of the API we have defined so far. Essentially a Postman collection is a group of saved requests for you to access quickly. Pretty much like the ‘Favorites’ in web browsers. To get a more detailed tutorial on collection creation: follow the steps here.
Create environments and use variables
One of the most popular features of Postman is to create environments, which allow you to define and reuse a set of variables. To get a more detailed tutorial: follow the steps here. In the case of our Dummy API, let’s create a local
environment wherein I specify the values of 3 variables: host
, port
, and itemId
.
Now, let’s parametrize the different parts of the URL in our GET and POST requests. First, open the request and select local
environment. Then, call the variables directly in the URL.
At any point, you can check the values assigned to each variables by hitting the “Environment quick look” button hidden in the top right.
Run uvicorn main:app --reload
in your terminal to expose the local API. Then you can fill in the request body of your POST request, hit Send and you should get a 200, something like this:
As soon as you deploy your application on multiple environments, this ‘environments’ feature will come in super handy. In one click, you can seamlessly switch between your environments (local, dev, prod, etc...), and under the hood Postman will update the values of all variables. Sticking to this example: the variable host
would switch from localhost
or 127.0.0.1
in the local environment to mydummyappdev.azurewebsites.net
for the dev environment (if you deployed in Azure for example). Same goes for port
, and many others potentially (API tokens, credentials, etc...).
Note that for sensitive information like an API token, you can set the initial value of the variable to null. And then each member of the workspace will have to add the token as “current value” of the variable. This is a more secure protocol because current values of variables are never synced to an account or a workspace, strictly to your active session.
API Testing
Write your own API tests in basic Javascript
Let’s say you just made a commit on your app, and you want to check that all your API routes are still functioning properly (no regression). For example, you might want to check that given a standard input, all routes return a 200 response.
The naive approach is to run each request one at a time and check visually that you get a 200. It gets long and tiring as you keep adding new routes and cases to your application. All the more if you have more than one check to run (status code, response delay, response body schema, etc...).
Enter Postman tests. In all Postman requests, you have a Tests tab. The syntax is Javascript-like, designed to be easily readable. To test that the response is a 200, you would do:
Now, you can add this script in the Test tab of each of the requests of your collection (GET and POST). Even better, you can add the test directly at the root of the collection (the collection folder has a Test tab as well). It will apply automatically to all individual requests of the collection. So when you run the collection as a whole you should get something like this:
AI-powered tests creation
If you don’t know what to test, or if you’re lazy and don’t want to write some code, you should try out the Postbot! Run your request once so that Postbot can analyze a sample response. And then you can ask it to generate some API tests for you.
Postbot has a natural tendency to write a ton of data validation tests. For instance, it will almost inevitably write a test called “Response has the respected fields”, here in our dummy API name
, description
, and price
. Note that performing data validation in Postman tests is not the best practice! We strongly recommend using Pydantic, especially allied to FastAPI.
Advanced API testing features in Postman
Postman provides many functionalities and code libraries to create sophisticated tests. For demonstration, let’s get back to our dummy API and write one. In our case, a nice end-to-end API test would be to POST an item with some given information and validate that afterward when you retrieve (GET), you get the exact same information. Ideally, you want that information to be randomized so that your API is not limited to a set of basic values (1, “abc”, etc...).
Generate random values and use them in POST request:
Verify the values obtained in GET request match:
Ready for running! Don’t forget to make sure that the POST request is executed before the GET when you use the collection runner. Two options to do that: 1) in Postman, you drag the POST request ‘above’ the GET request 2) you can use Postman’s setNextRequest command to enforce chronological orders between requests.
Postman API Tests automation
Postman collections are not just tools for manual testing; they seamlessly integrate into Continuous Integration/Continuous Deployment (CI/CD) pipelines. But what’s the point of integrating API tests in your CI / CD pipeline?
When you push a commit to your staging environment, the CI/CD pipeline runs unit & integration tests, and provided they pass, it triggers a build. So if the unit & integration tests pass, you consider your code (now in the staging environment) ready to be put into production. Yet, without integrated API tests, there's a blind spot.
Imagine a scenario where your team is developing an e-commerce application. You code a new feature that you successfully push to the staging environment. And incidentally, a new feature is added to the payment gateway integration. While the unit & integration tests still pass on your side, these tests don't cover the full spectrum of the API interaction.
That’s why you want to add API tests in your CI / CD pipeline! You can find more details about how Postman allows you to do so here.
Take things further with Postman
To take our API testing strategy to the next level with Postman, we should consider expanding into monitoring and load testing.
Postman's monitoring capabilities allow you to schedule and automate your tests to run at regular intervals, providing continuous checkpoints of your API's health and performance. In a context where your API relies on a few other evolving APIs, it’s not an extravagance. It ensures you address issues proactively, rather than reacting to problems after they impact users.
Another powerful feature of Postman to explore is load testing. Although not its primary function, Postman can be the starting point of stress testing your API. It will give you some precious insight into how your API performs under a heavy load of requests. For more sophisticated scenarios, integrating with dedicated load testing tools might be beneficial.
By incorporating increasingly advanced API testing practices, you'll elevate your API's reliability and gain insights into its scalability and resilience.
Want to read more about data engineering end-to-end tests? Check out this article on building data pipeline tests on Databricks.