XenonStack Recommends

Serverless

Contract Testing for Applications with Microservices

Navdeep Singh Gill | 20 September 2024

Contract Testing for Applications with Microservices
11:17
Contract testing strategies for Robust Microservices Applications

Introduction to Consumer-Driven Contract Testing

If you are working in the testing domain, you probably have heard about microservices. Many organizations that have moved into modern DevOps practices are also investing in converting or building new projects into Microservice-based architecture. Contract Testing in the Microservice architecture is about splitting the application into small standalone services, each of which plays a separate role in the overall system.

Microservices are a new technology that comes with certain challenges, especially in the testing domain. Testers, especially web testers, are now figuring out new automation testing approaches in the world of microservices. Before digging deeper, let’s understand what microservices are.

Benefits of Monolithic architecture

A Monolithic approach gives a boost a the beginning of the project. There is no need to define complex integration testing and deployment processes. Everything is trivialized to a single service.

Pros:

  1. Single codebase — simple to develop (at least in the beginning).

  2. No need to verify integration with other services since they don’t exist.

  3. Relatively easy deployment process — only one service needs to be deployed.

Cons:

  1. Single point of failure — if one app is down, the whole service is down.
  2. Maintenance complexity grows with time.
  3. Hard to adopt new technologies and techniques. Usually, it requires rewriting the application from scratch.
A/B testing is not only a one-off question or settling a disagreement; A/B testing is more than that. Source- A/B Testing Tools and Practices

Benefits of Microservices

Microservices have many advantages over monolithic architecture (where a single application takes responsibility for the whole system). The most important ones are listed below:

  1. There is no single point of failure. When one service is down, users can still use the application, and other services are still working.

  2. Individual Microservices can be scaled up to increase their capacity and availability.

  3. Security — most of the services aren’t exposed to the Internet.

  4. Every service can be deployed independently of other services. Providing they don’t break the API contracts.

Cons:

  1. Lots of work needs to be invested to set up the foundation.

  2. Deployment processes are more complex. Some deployments may require deploying more than one service.

  3. Tons of work on DevOps. Especially with deployments.

  4. Dealing with integration testing.

Most large applications are developed by hundreds of developers. That’s why splitting an application into smaller services makes work easier and faster. The Microservices architecture is a good choice for medium and large applications, although the most successful startups began their applications in monolithic architecture and migrated to Microservices later on. Before testing a microservice application, a developer needs to perform the following strategies

  1. Ensure the code branch is correct

  2. Pull the latest code from the repository

  3. Ensure dependencies are all updated

  4. Rerun if there is any database migration

  5. Start the service

Designing Microservices for Some Applications

Let’s consider we have different services in an application like Website, Android App, iPhone App, API Gateway, User Service, Job Service, Payment Service, and Email Service. The website and mobile apps communicate with services through the API Gateway. The API Gateway is the only way to access the services from the Internet. The services communicate with each other inside the internal network. Imagine a user creating a new account. They enter the website, fill in the form, and send the request to the API Gateway. The API Gateway delivers this request to the User Service that creates a new account. After creating an account, User Service sends a request to the E-Mail Service to send an activation e-mail. An e-mail is then sent with a link that the user needs to click to activate their account. As you noticed, the E-mail service isn’t available over the API Gateway. This is because only internal services can trigger e-mail notifications.

A process to check that the deployed build is stable or not, also helps a QA team to get confirmation so that team can proceed for further testing or not. Source- Smoke Testing

Consumer-Driven Contract Testing

Consumer-driven contract tests are actually integration tests that target your API, whether it’s REST-based or messaging-based. Imagine you’re working on an application that exposes its data using a REST API. Another team is using your exposed data for some functionality that they are providing. In order to guarantee that the functionality of the other team their application doesn’t break if we make changes to our API, we create a contract between the two teams.

The key in this setup is that the contracts are defined by the consumer of your API instead of the developer who wrote the implementation of certain functionality. With this approach, we can generate tests by using those consumer-driven contracts and verify whether we’re going to break any of our consumers’ applications. Instead of running integration tests between all the services — get rid of them. All services communicate through RESTful APIs. That means that if we define a tight “contract” between APIs, we don’t need to spin up the whole platform. It is enough to verify the fulfillment of the requirements of other services.

What is the Consumer?

  • A consumer is a client who wants to receive data from other services (for example, a web front end or a message-receiving endpoint).

  • They define requirements for the endpoint, such as HTTP headers, status code, payload, and response.

  • The contracts are generated during the unit test runtime.

  • After all tests succeed, pact creates JSON files containing information on HTTP requests.

What is a Provider?

  • A provider is a service or server that provides the data (for example, an API on a server that provides the data according to the client's needs or a service that sends messages).

  • Pact Provider Verifier is a tool for verifying contracts with providers. The verifier runs HTTP requests based on the contracts created by the consumer.

  • If the server response is expected tracing, it may sound like taking your contacts out of your eyes and then drawing circles around them. d by the consumer, the tests pass.

How to Deliver Contracts to All Peers?

After all tests on the consumer side succeed, the JSON files containing contracts are created. Our job is to deliver them to the providers to verify the contract. There are several ways of doing this, listed below:

Git repository for storing pacts, and including them into each project with a git submodule. In my opinion, the best way of doing it

  1. A file system.

  2. The Pact Broker

The Pact Broker is an application for sharing consumer-driven contracts and verification results. We can push our contracts there and allow the service providers to download them and run tests against them. DiUS provides a cloud version of the Pact Broker so, maybe you can take a look and use it. I don’t see too many advantages in running your own Pact Broker. In the end, it is yet another service that requires maintenance from DevOps. If you want to run by yourself, here are the Docker images. I think storing contracts in a separate git repository is good enough. Personally, I would create them during the integration pipeline and run tests on the service providers within the same integration job— for example, the integration pipeline here.

Why Should We Do Contract Testing?

  1. Quick Iterations: Enables rapid experimentation and the ability to discard unsuccessful ideas, preventing bad decisions.

  2. Complex Architectures: As architectures grow, identifying the source of issues becomes challenging, especially with multiple consumers.

  3. Integration Tests Limitations: While integration tests ensure safe service boundaries, they can be difficult and slow to execute.

  4. Role of Contract Tests: Contract tests help ensure that we meet the agreements established with our consumers.

  5. Change Rollout Risks: Quick changes can lead to overlooked contract testing, risking breaking changes in APIs.

  6. Responsibilities with Changes: Ensure new versions do not introduce breaking changes; create new endpoint versions if needed. Document updated APIs and write unit and integration tests.

  7. Consumer Dependency: Without control over all API consumers, their specific needs may become unclear.
  8. Integration Test Limitations: Integration tests might catch issues, but they won't clarify whether the problem lies with the consumer or the API.

  9. Consumer-Driven Contract Testing: Consumers usually have unit tests that run in isolation, mocking the API provider. These mocks specify the contracts expected from the API.

  10. Using Pact for Testing: Pact is a popular tool for consumer-driven contract testing, enabling

  • Client-side mocking

  • Sharing mocks with API providers to verify downstream compatibility.

A Holistic Strategy for Contract Testing

To summarize, contract testing means that we verify our API against a set of expectations (contracts). This means that we want to check if upon receiving a specific call, our API provider server will return the data we specified in the documentation. We often lack precise information on the needs of our API consumers. To overcome this problem consumers can define their expectations as mocks that they use in unit tests, creating contracts that they expect us to fulfil.

We can gather these mocks and verify that our provider returns the same or similar data when called the same way as the mock is set up, essentially testing the service boundary. This approach is called consumer-driven contract testing t aking the following step A bit of software that does something specific is sometimes called a service provider. Something that needs that service is a service consumer. If a client wants data from an API the API is the provider and the client is the consumer. This communication is done under a contract - a set of specifications by which the consumer uses a provider. CDC testing is where you write the tests from the consumer’s point of view.Contract Testing in the digital world helps companies to develop and deliver software services which further help to serve the clients efficiently. To know more about Contract Testing,  we advise learning more About our Microservices Testing