Any piece of software is only functional if it can receive messages (queries or commands) from an outer source. A software library receives calls from another part of the application. A backend service receives calls from the front end, the user interface.
User interfaces are notoriously difficult to test. Meanwhile, the API, the outer shell of our application, is still relatively easy to test. API testing entails running automated tests on our API. It's a part of integration testing and is usually performed by developers and/or a build server, though testers can help with the test design.
In this article, we'll explore API testing, what it is, how it differs from other types of tests, and what the advantages of API testing are. We'll also cover the different types of API tests, how to perform them, and some best practices.
API Testing vs. Unit Testing
Unit testing is the act of writing automated tests for relatively small units of code. How small these units are differs from team to team, but developers will often write unit tests for single functions. These are functions that are not publicly available to the user interface on their own, and their unit tests check the inner details of the system.
As developers construct the system, these units start to interact with each other. When that happens, Developers move on to integration tests to test the correct integration of the different units. API tests are integration tests at the outermost level, just before we include the user interface.
API testing is also much broader than testing the integration of the underlying units. Under the umbrella of API testing, we can also test APIs that the application consumes instead of produces. We can even test if the API reacts fast enough or responds well to security attacks. We'll look at the different kind of API tests later in this article.
Why Is API Testing Important?
The different kinds of API tests we already mentioned tell us why API tests are important, even if we already have unit and integration tests. The API is the layer that is susceptible to security attacks. This layer includes all underlying logic and services that add up to the total response time. And of course, it's the layer in front of the entire backend, and it allows access for testing the bulk of our application.
It's a good practice to keep the user interface layer as lightweight as possible. This allows us to more easily change the UI, which is a field that evolves very fast. If all business and validation logic is behind the API, our API tests can verify that our application still (mostly) works as expected. That's why API testing is essential in modern software development.
Benefits of API Testing
We've already hinted at some advantages of API testing, but let's look at them in more detail.
API Tests Aren't Tied to One Language
Unit and integration tests make calls to internal pieces of the software. As such, these tests are usually written in the same programming language as the application code. At the very least, they must run on the same platform. But we can write API tests in different languages or run them on different platforms.
A REST API written in C# can still be called by API tests written in Java, or by using a tool like Postman of Insomnia. APIs very often accept messages in a standard format like JSON, XML, CSV, or one of many others. This gives teams the opportunity to choose the API testing tool that best fits their needs.
API Tests Can Be Done Before GUI Testing
We can write our API tests before we even have a GUI. In some cases, there won't even be a GUI, or it'll be developed by another team or even another company. In each case, we can put as much application logic as possible under the API layer and test the bulk of our application before work on the GUI is started. This allows us to verify our user scenarios early on.
API Tests Are Easy to Maintain
UI tests are notoriously difficult to maintain. User interfaces change constantly because of user and tester feedback. These changes don't always require changes to the API and the API tests, but they will require changes to the UI tests. To be complete, we must perform UI tests on a large range of devices, resolutions, and screen orientations. In contrast, API tests can be automated easily and only need to change if the expected outcome changes, which is a lot less often than is the case with UI tests.
API Tests Are Fast and Offer Broad Test Coverage
Because there's no need to test the API on different devices, API tests run a lot faster than UI tests while still offering broad test coverage. If set up correctly, API tests can cover a very large portion of the application in a limited amount of time. This gives API tests the advantage of finding bugs in less time.
API Tests Enable Faster Releases
Because of their speed and versatility, API tests fit well in an agile and DevOps culture, where short feedback loops are important to ensuring fast, regular, and stable releases. Without a significant test suite that has broad test coverage, we can't be confident enough that our new release doesn't contain bugs.
With API tests (and of course unit tests and integration tests), we can run our entire test suite after making changes to the code and before releasing the new version. This allows us to release weekly, daily, or even hourly.
Types of API Tests
Now that we know what API testing is, why it's important, and what benefits it brings us, let's explore the different kinds of API tests.
Functional testing, sometimes known as validation testing, is done by calling the API with certain inputs and verifying the output is what we expect it to be. Functional testing doesn't care about how the system came to this output. The system is a black box which we can't look into. But as long as the software gives us the correct results, functional API tests will pass. Functional testing intends to check if the system responds as the client expects.
When we want to know how well our system performs under a heavy load, we need to include all bits and pieces of the application. That's why load testing is a type of API testing. It doesn't verify the system functionally, in that it doesn't check whether the responses are what the client expects them to be, but rather, it stresses the system to see if and when it breaks down. It can also check for when it fails to respond fast enough, or when it fails to respond at all.
Modern software systems can be subject to many different kinds of security attacks. The API is a popular entry point to attack an application. Security testing is a type of API testing that verifies that the API is secure and can handle known attack vectors. Security testing can be done automatically by a tool or manually by a human. It includes inspection of authentication, authorization, and encryption.
Penetration testing is a specific type of security testing. In a penetration test, an authorized hacker will try to attack the system from outside in the same way a real hacker would: by making use of exploits in the API of the application.
Fuzz testing is another type of security testing. In fuzz testing, testers provide large amounts of invalid or random data to the API. The testers then check the application for behavior that shouldn't occur, like exceptions, crashes, or memory leaks.
Runtime Error Detection
This entails monitoring the application during its execution. We can perform it during any type of test (from unit tests to GUI tests), but it's very useful during API testing. Runtime error detection on the API can pick up on issues with the application that are more difficult to find during lower-level tests. Errors like memory leaks, race conditions, and buffer overflows are examples of errors that only occur at runtime.
How To Perform API Testing
Before you can start API testing, you'll need a working API. This is your API testing environment. This means you can run the application somewhere and have it integrate with all the necessary components: external services, databases, messaging systems, file servers, etc. The application can run on the developer's machine if they want to test any changes they made before committing them to source control. But you'll also want to run API tests against a system that remains up and running. This can be used in daily builds and is useful in detecting errors, like memory leaks, that occur on long-running systems.
A next step is to design your test cases. Define the input parameters and the expected response. Optionally, make a trial API call manually and analyze the result. But then you should formalize this into an API test that you can automate, so you can run the tests more easily in the future. Ideally, you'd integrate these API tests in your daily build so that you constantly check your API as development continues.
API Testing Best Practices
I already mentioned two best practices for API testing above: automation, and integration into the build server. You need the first before you can implement the second. But doing so provides the great benefit of receiving alerts early when things have broken. The team can then resolve the issues before they make it to the end-user. When starting with API testing, start small. Think about smoke tests and limited "happy path" tests. After that, you can move on to API tests that require multiple steps, tests that verify edge cases, and testing invalid data input.
In some cases, you should consider mocking external components. If you want to test how your API reacts to an external service being down, you'll have to mimic this situation. Another best practice is to include non-functional tests, like security tests and load tests. These also impact users, but sometimes teams forget about them until it's too late.
API testing is an important part of agile software development and DevOps. Both testers and developers can and should engage in API testing. The different types of API tests all have their benefits and should all be considered as part of your system.
We can only execute API tests when we have an API available, but work on the API tests will continue as the application evolves. API testing is the act of testing the application at the layer just below the user interface.
This post was written by Peter Morlion. Peter is a passionate programmer that helps people and companies improve the quality of their code, especially in legacy codebases. He firmly believes that industry best practices are invaluable when working towards this goal, and his specialties include TDD, DI, and SOLID principles.