6 minutes read
Consumer-driven Contract Testing (CDCT) is a software development methodology that focuses on ensuring that the interactions between microservices and APIs are predictable and reliable. One of the key tools used in CDCT is Pact, an open-source framework that allows developers to create and manage contracts between services. In this blog post, we’ll explore what CDCT is and how Pact can be used to implement it.
What is Consumer-driven Contract Testing?
CDCT is a testing approach that shifts the responsibility of defining the expected behavior of an API from the provider to the consumer. This approach ensures that the consumers of an API are able to test their applications against a defined contract before the provider makes any changes. By doing so, CDCT helps to prevent unexpected changes to the API from breaking the consumer’s applications.
Why Use Pact in CDCT?
Pact is a popular choice for implementing CDCT because it provides a comprehensive framework for creating and managing contracts between services. Pact provides a range of features that make it easy for developers to test their applications against the contracts defined by the consumers of the API. Some of the key features of Pact include:
- A simple and intuitive syntax for defining contracts
- Support for multiple programming languages
- Ability to run tests locally and in CI/CD pipelines
- Automated contract verification
- Integration with popular testing frameworks
How to Implement CDCT with Pact
Implementing CDCT with Pact involves the following steps:
- Define the contract: The first step in implementing CDCT with Pact is to define the contract between the API provider and the consumer. This contract defines the expected behavior of the API and the format of the data that will be exchanged.
- Create tests for the consumer: Next, the consumer of the API creates tests based on the contract. These tests will verify that the consumer’s application is able to correctly interact with the API and that the expected data is returned.
- Verify the contract: Once the tests have been created, the consumer runs them and Pact generates a pact file that captures the interactions between the consumer and the API. This pact file can be shared with the provider to verify that the contract is being upheld.
- Verify the provider: The provider can then use the pact file to verify that their API is functioning as expected and that it meets the expectations defined in the contract. If there are any discrepancies, the provider can make the necessary changes to ensure that the contract is upheld.
- Update the contract: Once the provider has made the necessary changes, the consumer can re-run their tests and update the pact file. This process can be repeated until both the consumer and provider are satisfied that the contract is being upheld.
Benefits of Consumer-driven Contract Testing with Pact
CDCT with Pact offers several benefits, including:
- Improved reliability: By testing the interactions between services before they are deployed, CDCT helps to ensure that the interactions are reliable and predictable.
- Faster development: CDCT helps to speed up the development process by allowing developers to work in parallel and reducing the time spent on integration testing.
- Better communication: CDCT promotes better communication between the provider and consumer, helping to ensure that both parties have a clear understanding of the expected behavior of the API.
- Early detection of issues: By testing the interactions between services before they are deployed, CDCT helps to identify and resolve issues early in the development process.
Here’s an example of a consumer contract test using Pact in Java:
import au.com.dius.pact.consumer.MockServer;
import au.com.dius.pact.consumer.Pact;
import au.com.dius.pact.consumer.PactTestExecutionContext;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.model.RequestResponsePact;
import org.junit.Rule;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class ConsumerContractTest {
@Rule
public PactProviderRuleMk2 provider = new PactProviderRuleMk2("provider", this);
@Pact(consumer = "consumer")
public RequestResponsePact createPact(PactDslWithProvider builder) {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json;charset=UTF-8");
return builder
.given("test GET")
.uponReceiving("GET REQUEST")
.path("/test")
.method("GET")
.willRespondWith()
.status(200)
.headers(headers)
.body("{\"response\": \"test\"}")
.toPact();
}
@Test
@PactTestFor(pactMethod = "createPact")
public void runTest(MockServer mockServer) throws IOException {
assertEquals(new ConsumerClient(mockServer.getUrl()).get(), "{\"response\": \"test\"}");
}
}
And here’s an example of a provider contract test using Pact, also in Java:
import au.com.dius.pact.provider.junit.PactRunner;
import au.com.dius.pact.provider.junit.Provider;
import au.com.dius.pact.provider.junit.State;
import au.com.dius.pact.provider.junit.loader.PactBroker;
import au.com.dius.pact.provider.junit.loader.PactBrokerAuth;
import au.com.dius.pact.provider.junit.target.HttpTarget;
import au.com.dius.pact.provider.junit.target.Target;
import au.com.dius.pact.provider.junit.target.TestTarget;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
@RunWith(PactRunner.class)
@Provider("provider")
@PactBroker(host = "localhost", port = "80", authentication = @PactBrokerAuth(username = "username", password = "password"))
public class ProviderContractTest {
@TestTarget
public final Target target = new HttpTarget("http://localhost:8080");
@BeforeClass
public static void setUpService() {
// Start your service
}
@State("test GET")
public void testGetState() {
// Set up the state for the test (e.g. database, mock server, etc.)
}
}
In conclusion, Consumer-driven Contract Testing with Pact is a valuable approach for ensuring the reliability and predictability of the interactions between microservices and APIs. By shifting the responsibility of defining the expected behavior of an API to the consumer and using Pact to manage the contracts, developers can improve the quality of their applications and speed up the development process.