Diving deeper into API testing
Updated: Jun 11, 2020
I’ve explored UI testing in a fair amount of detail in my last two articles and now I want to turn my attention to API testing. Although I decided to do UI first, that doesn’t mean that APIs are less important from a testing perspective In fact, depending on the architecture of your system, API testing is often where the majority of your functional testing should be focused.
Whereas the UI represents the look and feel of a particular application, an API is often where most of the functional processing happens and why testers should spend a lot of their time testing and automating in this department to be able to cover the greatest amount of coverage most efficiently.
APIs serve as the integration points for the majority of software, receiving information, processing or transforming it and then often either returning it or passing to another system. This important aspect of the system where lots of logic is handled is therefore critical to get right and test appropriately and unlike UI testing, this is not something where you want to apply a risk-based testing approach and only tests what’s most critical. At an API level, you want to test as much as possible, meaning that you need to understand the details of how it works all the more important.
Following these below points should help to ensure you approach your API testing correctly. In this article, I will focus on just the aspect of testing your API and will then go into detail on the actual automation of the API in my next article.
Understanding the requirements of the API
Often when analysts focus on their business requirements, they look at it from a purely business perspective meaning that the UI often tends to be more well documented with requirements than an API. This is something which is sadly mistaken as APIs are actually where you want to focus the most on in getting your requirement right. The good news is though that its also the part of the software that can be most easily defined. And the added benefit I that doing it early doesn’t just aid in the testing effort and allow the tester to better write test cases, but with APIs more suited to test-driven development than any other part of a software’s architecture, it means the developer can easily build their code to meet the test requirements saving a lot of testing issues further down the line.
Before writing tests for an API you will want to know the following about the API to ensure your requirements have captured all the relevant information:
What is the purpose of this API?
What is the workflow of the application? How does it function and what does its architecture look like?
What are the integrations points of the API? Understand all the upstream and downstream system it works with and what is required from each
What are the features and functions of the API?
What are expected valid and invalid inputs of the API?
How should the API behave when invalid messages are received?
Answering these questions will not only help you know ho to test your system but hopefully design it as well and in answering these questions you are likely to identify issues or gaps in your design that may need to be rectified and you would rather want to do there than find it out later during integration testing, so even if this design work takes a lot of extra effort the payoff is definitely worth it.
This information should be clearly documented, particularly the expectations around the different input and output validations and the format of the API messages.
Build a message format
Having the basic requirements and overall flow for your API message sorted out is great, but its important to at this point build out how your API messages ill actually look. You have all of the different permutations for testing and development identified in the requirements, but it is helpful to ensure that the overall structure is also well mapped out. This is a time-consuming exercise, but in getting this right, you reduce your development work considerably. It makes it easy for developers to know what to develop towards, makes it easy for testers to write the necessary tests in advance and where dependencies already exist, already start testing again those so you can iron out all of your integration points before development is even finished.
Write all the expected input and output messages for your APIs
Now that you have all the relevant information, its important to document what your API messages and their test criteria will look like. You shouldn’t need to wait for an API to be coded to do this but should start it straight away. The input/output of the API tests can vary from pass/fail status to any relevant or invalid data or a call to some other API. The important thing is to be as thorough as possible.
Even if you aren’t trying to automate your API, doing this will add some structure to what is needed and form the basis of the developer’s design and knowing what their relevant output should look like. The importance of doing this is not just for your current API through but also for its other integration points, especially with existing APIs. You can use these to validate that the existing infrastructure and your understanding of these API messages is correct can prevent any issues before coding is too far along.
Concentrate on Small API Functions
API testing is not a type of testing in which you can directly jump on writing bigger test cases, especially if they cover multiple integration points. So, creating small API test cases or API calls is less painful and likely to give you more immediate results. In small API functions, it then becomes a lot easier to write small API test code and test whether the output is expected or unexpected at each relevant point in an API, rather than waiting for entire integrations to be completed.
Later on, clubbing or including dependencies of the various tiny successful API functions will then be a lot more effective because of all of the detailed testing would’ve already been done, allowing you to focus on positive scenario testing only.
For example, say you want to test the user authentication of an application. If the authenticity of the user is valid, then you want to trigger a password change functionality. While performing API testing here using SOAP/REST, you write two different sets of test cases for two different functions, like user authenticity and password reset. Once you bring the functions together you only need to test one pass through to the second function, saving you a lot of time at an integration level.
Employ Security Testing
Because APIs are integration points to the software, they are key security vulnerability areas. Even though most users many interact via a UI, it’s the APIs that receive the messages and where you would want to block any unauthorised or potentially dangerous message. So, once you’re done defining all of your functional scenarios, its best to start looking at the security aspects of your API.
To do this, you will want to check your API against the following:
Various types of SQL, HTML, or code injections. These are often the easiest to test against, as most people should be aware of them and many security tools will test for these automatically.
Insecure message connections (your API should not receive a message from certain unsecured APIs)
Incompatible versions that do not form part of its dependencies – This is an API that is on the list of valid dependencies, but with n out of date or incorrect version.
OS commands – you want to ensure your API detects these and that your OS does not respond as a result
Authentication token management – Many sites will make use of an API token that gets sent to ensure a user is validated on a system. You will want to test with a refreshed token or token changes throughout to ensure all entry points can identify the manipulation or duplication of a token ID and its validity.
Security testing itself is quite extensive and I will be writing up another article later to go into more detail on how this works. However, this is not something you should ever ignore even if you feel that your API is internal only and therefore not exposed to external attacks.
Measure the performance of the API
APIs might be the functional meat of the system but given that they also serve as the primary flow of information they are central to the overall performance of a system and as such should be performance tested. You don’t need to wait for the completion of an integrated system to do this effectively either. You can easily do this once an API is completed and you can performance test with either mocked or unmocked endpoints, pending on the status of the other systems it needs to interact with. Importantly though you want to measure the performance of each individual API in a flow to address the specific performance gaps in that specific API.
To do this, you will want to test how long it takes for an API to execute once it receives its message (start), processes and passes it on to another (end). All APIs are going to be dealing with different data and system though, so it’s important that you have a certain level of expectation of an API to determine whether the performance tests are meeting expectations. You should also have an expectation for the different load expectations of each API to ensure that you test for these too.
Like security testing, there is a lot to performance testing which I will go into at a later time, but what is important is to not leave this too late. Don’t wait for an entire system to be developed before trying to load and performance test your system. These can be done the moment any given API is complete and lightweight performance test even added to your pipeline, to ensure they remain performant throughout its lifecycle.
There is no doubt APIs are an important functional component of any modern software architecture and so you want to invest the effort to ensure you are testing these correctly. My next article will dive deeper into how you can also automate your APIs so that you can get your APIs automated quickly and form the platform for Continuous Delivery (CD) through your network of APIs.