Skip to Content
Course content

289: Contract Testing Basics

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By the time you get to this stage in a project, you've probably felt the pain of a "breaking change." You update a field in your Payment Service, deploy it, and suddenly the Order Service starts throwing 500s because it was expecting a transaction_id but you renamed it to txId. It's frustrating, and it's exactly why we use contract testing.

Wait, why can't I just use a big integration test suite?

I get this question a lot. If you have an integration test that spins up both services, doesn't that catch the bug? Yes, it does—but at a massive cost. Integration tests are slow, they're flaky, and they require a complex environment to run. If you have twenty microservices, you can't possibly spin them all up on every commit without your CI pipeline taking three hours.

Contract testing flips the script. Instead of testing the implementation of the connection, we test the agreement. The "contract" is just a JSON file that says: "If the Order Service sends a GET request to /payments/123, the Payment Service must return a 200 OK with a body containing a string called status." As long as both sides adhere to that file, you don't actually need to run them at the same time to know they'll work together.

How do I actually define a "contract" in Java?

In the Java ecosystem, Pact is the go-to tool for this. It uses a "Consumer-Driven" approach, meaning the service consuming the API (the Order Service) defines what it needs. I prefer this because the provider shouldn't be guessing what the consumer wants.

Here is a simplified look at how you'd define a contract for a payment lookup using the Pact DSL:


@Pact(consumer = "OrderService", provider = "PaymentService")
public RequestResponsePact createPaymentPact(PactDslWithProvider builder) {
    return builder
        .given("Payment with ID 123 exists")
        .uponReceiving("A request for payment 123")
            .path("/payments/123")
            .method("GET")
        .willRespondWith()
            .status(200)
            .body(new PactDslJsonBody()
                .stringType("transactionId", "TXN-789")
                .stringType("status", "COMPLETED")
                .decimalType("amount", 99.99))
        .toPact();
}

Notice that I'm using stringType instead of a hardcoded value. This is key. I don't care if the ID is "TXN-789" or "TXN-001"; I just care that it is a string. This prevents your tests from becoming too brittle.

What happens when the provider actually breaks the contract?

Once the consumer runs the code above, Pact generates a JSON "pact file." This file is sent to the provider (the Payment Service). The provider then runs a verification test that essentially replays every request in that JSON file against its own local controller.

If you renamed transactionId to txId in your Java POJO, the provider's test will fail immediately. The error message will tell you exactly what went wrong: Expected body to contain 'transactionId', but found 'txId' instead.

The beauty here is that the failure happens in the Payment Service's own build pipeline, before the code ever reaches a staging environment or, god forbid, production. You catch the mismatch at the source.




📋 Practical Task

Exercise: Validating the Payment Status Contract

You have been provided with a Pact JSON file generated by the Order Service. The Order Service expects the Payment Service to return a status field (String) and a timestamp field (ISO 8601 String) when querying a payment.

Your task is to implement the PaymentController in the provided Java project so that it satisfies the contract. Currently, the controller returns a PaymentResponse object, but the fields are named paymentStatus and date.

  • Modify the PaymentResponse POJO to match the expected contract field names.
  • Ensure the timestamp field is returned as a String in ISO 8601 format.
  • Run the PactVerificationTest class to confirm that the provider now satisfies the consumer's contract.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.