• Skip to main content
  • Skip to secondary menu
  • Skip to primary sidebar
  • Skip to footer
  • Home
  • Contact Us

All about code

  • Java
    • Spring
      • Spring Boot
      • Spring Cloud
    • Java Miscellaneous Tips
    • Java Design Patterns
  • Data Structures and Algorithms
    • Algorithms
    • Data Structures
  • Thoughts and Stories
  • AI

How to Implement Contract Tests with Pact

penmanship pen signature document 2561217

Introduction

When we develop a micro-service system with many REST APIs, we face the problem of testing it. In addition to unit, integration, and end-to-end tests, we must encompass contract testing. Contract testing involves verifying how the interfaces provided by a service satisfy the provider’s requirements. In this article, we will see how to implement contract testing with the Pact software.

A Few Words on Pact

Pact is a tool for performing contract-based testing. It is consumer-driven, which means that it bases the “contract” on the consumer interface. Once the contract is written and stored in a shared place, the producer and consumer can test it independently.

We can use two Junit5-based packages that implement Pact for consumers and producers. A maven plugin is also available to generate the contract in the consumer part. To store the contract we can use a Pact broker. In this case, we can configure the plugin with the host and port of the broker.

Pact Configuration on the Consumer

Suppose we have a Book service with a getAuthor method, that calls an Author service to retrieve some information about an author by its name:

    @GetMapping(value = "/findAuthor", params = { "name" })
    public Optional<Author> getAuthor(@RequestParam("name") String name) {
        return bookService.getAuthor(name);
    }

To support Pact we need the following Maven dependencies:

		<dependency>
		    <groupId>au.com.dius.pact.consumer</groupId>
		    <artifactId>junit5</artifactId>
		    <version>4.6.10</version>
		    <scope>test</scope>
		</dependency>
		<dependency>
		    <groupId>au.com.dius.pact.provider</groupId>
		    <artifactId>junit5spring</artifactId>
		    <version>4.6.10</version>
		    <scope>test</scope>
		</dependency>

We also add the following, to support Assertj assertions:

		<dependency>
		    <groupId>org.assertj</groupId>
		    <artifactId>assertj-core</artifactId>
		    <scope>test</scope>
		</dependency>

We also add a Maven plugin. It generates the contract in JSON format and stores it in a Pact broker:

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <systemPropertyVariables>
                        <pact.rootDir>target/pacts</pact.rootDir>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
			<plugin>
			 <groupId>au.com.dius</groupId>
			 <artifactId>pact-jvm-provider-maven_2.11</artifactId>
			 <version>3.5.24</version>
			 <configuration>
			   <pactDirectory>target/pacts</pactDirectory>
			   <pactBrokerUrl>http://localhost:9292</pactBrokerUrl>
			   <trimSnapshot>true</trimSnapshot>
			</configuration>
			</plugin>

We can launch the Pact broker by Docker, using a docker-compose file like the following:

version: '3'
services:
  postgres:
    image: postgres:13
    environment:
      POSTGRES_USER: pactbroker
      POSTGRES_PASSWORD: password
      POSTGRES_DB: pactbroker
    volumes:
      - postgres_data:/var/lib/postgresql/data

  pactbroker:
    image: pactfoundation/pact-broker
    depends_on:
      - postgres
    environment:
      PACT_BROKER_DATABASE_ADAPTER: postgres
      PACT_BROKER_DATABASE_USERNAME: pactbroker
      PACT_BROKER_DATABASE_PASSWORD: password
      PACT_BROKER_DATABASE_HOST: postgres
      PACT_BROKER_DATABASE_NAME: pactbroker
      PACT_BROKER_PORT: 9292
    ports:
      - "9292:9292"

volumes:
  postgres_data:

As we can see from the above, the Pact broker uses a Postgres database behind the scenes.

Test implementation

We implement the test for the consumer part by providing the PactConsumerTestExt class to the @ExtendWith annotation. We define a createPact method to create and return the contract. In this case, we define how the findAuthor service behaves. Then we run a test on our contract definition by using the @PactTestFor(pactMethod = “createPact”) annotation:

@ExtendWith(PactConsumerTestExt.class)
public class PactConsumerDrivenContractUnitTest {

    @Pact(consumer = "ConsumerService", provider = "ProviderService")
    public V4Pact createPact(PactDslWithProvider builder) {
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");

        return builder
          .given("test GET")
            .uponReceiving("GET REQUEST")
            .path("/authors/findAuthor")
            .query("name=Goethe")
            .method("GET")
          .willRespondWith()
            .status(200)
            .headers(headers)
            .body("{\"name\":\"Goethe\",\"biography\":\"Bio info\"}")
            .toPact(V4Pact.class);
    }

    @Test
    @PactTestFor(pactMethod = "createPact")
    void runTest(MockServer mockServer) {
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(mockServer.getUrl() + "/authors/findAuthor?name=Goethe", String.class);
        assertEquals("{\"name\":\"Goethe\",\"biography\":\"Bio info\"}", response);

    }
}

Pact Configuration on the Producer

On the producer side, we set the same dependencies in the pom.xml, but we don’t need the maven plugin to generate and store the contract as on the consumer side. We define a test providing the Pact broker URL and marking the class with @Provider(“ProviderService”):

@Provider("ProviderService")
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@PactBroker(url = "http://localhost:9292")
@AutoConfigureMockMvc
public class ProviderPactTest {

    @Autowired
    private MockMvc mockMvc;

    @TestTemplate
    @ExtendWith(PactVerificationInvocationContextProvider.class)
    void pactVerificationTestTemplate(PactVerificationContext context) {
        context.verifyInteraction();
    }

    @BeforeEach
    void setup(PactVerificationContext context) {
        MockMvcTestTarget testTarget = new MockMvcTestTarget(mockMvc);
        context.setTarget(testTarget);
    }

    @State(value = "test GET", action = StateChangeAction.SETUP)
    void testGetExists() {
    }

}

Conclusion

Pact is a very useful tool to implement contract testing. An implementation of Pact features is available in Spring for the consumer and provider side. You can find the source code of the example of this article on GitHub.

Primary Sidebar

Social

  • Facebook
  • LinkedIn
  • Twitter

Archives

  • May 2026
  • April 2026
  • March 2026
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • December 2024
  • October 2024
  • August 2024
  • July 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • September 2023
  • May 2023
  • March 2023
  • January 2023
  • November 2022
  • September 2022
  • August 2022

Recent Posts

  • Better Avoid Spring Boot Lazy-initialization @PostConstruct Trap
  • How To Handle Large Datasets in Spring Boot
  • How to Log HTTP Incoming Requests in Spring Boot
  • How to Reliably Implement Post-Commit Actions in Spring
  • Brief and Easy Explanation of HATEOAS

TAGS

Algorithms Apache POI Backtracking Date Structures Dynamic Programming engineering Graphs Greedy Horror Stories HSSF Java Java New Features Java Principles Java tips real life Sorting Spring Boot Spring Cloud Strings Trees

Footer

Privacy Policy Cookie Policy Terms and Conditions