Contract Testing and API Testing: What You Need to Know

Why contract testing or API testing itself isn't enough, and how you approach it the right way. Read the whole article now.

Software TestingSeptember 24, 2024
Contract Testing and API Testing: What You Need to Know

APIs are everywhere, you use them every day without even realizing it. From checking your bank balance on an app to ordering food online, APIs make the digital world click. But here’s the catch: while developers rely on APIs to make software work, testers have a tougher question to answer, how do you make sure those APIs don’t break your user’s experience?

Before starting, let’s look at API and API testing. If you’re a pro in that, skip the first three paragraphs and dive directly into the juicy part.

What is API?

API stands for application programming interface. It enables the communication and data transfer between two software systems.

Little side note: Every developer develops toward an API since the computer exists. Every computer library exposes its functionality via an API. Therefore, every software developer is an API tester or at least a consumer 😉

Today, the typical kind of API technology-wise is REST API or GraphML API, where you request data stateless via internet protocols.

What is API testing?

The APIs need to be tested to ensure the programming interface's functionality, reliability, performance, and security. API testing is a type of software testing. From a software architecture perspective, it's performed on the business logic layer.

API testing vs. contract testing

Contract testing is a different name for API testing. There are different opinions out there about the definition.

Some say it's a lightweight form of API testing; others even combine the names to API contract testing.

Recently, everybody seems to be talking about contract testing. The hype comes from the bad reputation end-to-end (E2E) testing often carries. Many testers and managers see E2E as slow, flaky, and overly dependent on the UI layer, which makes it harder to set up and maintain. Contract testing promises a lighter alternative: remove the UI from the picture and test the APIs directly.

This trend has its roots in the rise of microservices. Modern applications are not built as one giant block of code anymore. Instead, they are made up of dozens or even hundreds of microservices, each responsible for a specific function. These microservices communicate through APIs, so the idea naturally follows: if the APIs work, the system should work too.

That thinking makes contract testing look attractive because:

  • APIs reflect the business logic, so testing them seems to cover the core functionality
  • Microservices are isolated, so they can be validated independently without relying on the UI
  • There is less overhead, since you do not need to spin up the UI just to verify data flow
  • Automation is easier, as lightweight contract tests integrate smoothly into CI/CD pipelines

The problem is that contract testing only validates the microservices, not the full application. Bugs that impact the user often go undetected because the user never interacts with APIs directly. They interact with the user interface, which has its own logic and potential issues.

Before exploring this further, let’s step back in time and look at a similar case from the 90s: the database testing problem

The database testing problem

In the past, the idea was to have the logic close to the data. First, there were databases and then database management systems.

The difference is that data is stored in the database, and authorizations and logic are stored in the database management system. They're known as stored procedures.

You don't add data with highly transactional data; you use stored procedures. For example, you say you want to add a credit card, and the stored procedure does it for you with the entered data. This means that the logic for the data is in the same system as the data. As the logic is already in the database, the conclusion was that you only need to test the database.

However, the customer doesn't call stored procedures on the database directly. He works with a graphical user interface that calls the stored procedures from the database.

It's the same situation as with API, aka contract testing today. There is an application that calls the APIs, but the user doesn't do it directly.

You could argue that the business logic is abstracted from it. But the actual logic isn't abstracted. The API doesn't cover things like the following:

  • What happens if the connection is lost?
  • What happens if the returned results aren't as expected?
  • How is versioning between server and client handled?

What is missing in contract testing

It is often overlooked that even if the logic sits in the controller, meaning the business logic is separated, the application itself still only calls different endpoints. It does not hold the actual logic, so it never covers the full story. What gets left behind is the user perspective and the real interactions that happen in practice.

You are not testing an application just to move a task to “done” on a board. You test to make sure everything works as expected, so that your users stay happy, your revenue is protected, and your reputation remains intact.

If you look closer, calling endpoints is really a workflow that needs to be tested. There are ways to recreate this with Postman scripts, but that approach essentially means you are rebuilding the software outside of the software itself. In the end, the real application never gets tested.

That is not to say contract testing has no value. It absolutely does. It can even replace certain end-to-end tests in some cases. The issue comes when it is used in isolation. On its own, it will not deliver the results you are aiming for because:

  • It ignores the user perspective
  • It misses how the UI interacts with the APIs
  • It does not cover application-specific logic
  • It risks leaving customer-facing bugs undetected

Contract testing is a useful tool, but it only becomes powerful when it is part of a broader testing strategy.

Why end-to-end testing can't be replaced by contract testing

To recap, the end-user doesn't call any API; he/she works with the application's user interface, which calls the APIs. The application has logic itself, which is not covered by the API. Usually, this happens at the client the end-user uses:

  • How do I access the API?
  • What are the secrets to call the API?
  • What are possible result values?
  • You need latency strategies, in case the network is slow
  • You need caching in your application.

All of this is not tested via API testing and might lead to severe problems on your customers' site.

TestResults API testing as part of the end-to-end software testing

We're a bit old-fashioned and stick to the established name API testing. With TestResults, you get model-based API testing. There are no raw calls; it's always an abstraction of the application behind.

For example, it allows the test case to say: "I want to create a login," and the login itself knows which API calls it needs to make. You don't need a Postman script with five calls to execute; the model abstraction in TestResults handles this for you.

A great way to use API testing is in combination with UI testing. This way, you can create test data through the API and then check in the UI how you can interact with this data.

End-to-end testing expanded with the options of API testing. You get the best of both worlds. Book a demo and see how it looks in action.

Automated software testing of entire business processes

Test your business processes and user journeys across different applications and devices from beginning to end.