Pact (contract testing)
Integrating with Java
Pact testing a Camel/Spring Boot application - example
This example shows how to implement Provider-side contract testing of a REST API which has been implemented using Apache Camel/Red Hat Fuse on Spring Boot. This will retrieve a contract from the Pact Broker:
- The Consumer first defines the contract, using the appropriate client-side tooling (e.g. for Angular)
- Add the Pact JVM Provider dependency to the Provider project.
- Add a test class (
*IT
) to the Provider project. Use the annotations provided bypact-jvm-provider
to download the relevant contract from a Pact Broker. Use theSpringRestPactRunner.class
runner. - Configure Maven Failsafe Plugin to run the contract test during the Maven integration-test phase and publish results to the Pact Broker.
First add dependencies to the POM:
<dependency>
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-provider-spring_2.12</artifactId>
<version>${pact-jvm-provider.version}</version>
<scope>test</scope>
</dependency>
Construct a contract test class:
- Replace any components that talk to downstream applications (e.g. databases, etc) with Camel mock components
- Configure the mock components to return appropriate fake data
- Build a test case for each
State
in the Pact contract.
An example test class might look something like this:
@RunWith(SpringRestPactRunner.class)
@Provider("provider-name-given-in-pact-broker")
@PactBroker
@SpringBootTest(
classes = CatalogueSearchApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
@UseAdviceWith
@MockEndpointsAndSkip("bean:my-data-store")
public class MyApplicationProviderContractIT {
@EndpointInject(uri = "mock:bean:my-data-store")
MockEndpoint mockMyDataStore;
@TestTarget
public final Target target = new SpringBootHttpTarget();
/**
* Implements what the application should return when the following Pact
* interaction is executed:
* "state name as defined in the pact contract"
*/
@State("state name as defined in the pact contract")
@Test
public void lookupUsingQueryParams() throws Exception {
// Configure the mock bean to return some fake data
mockMyDataStore.whenAnyExchangeReceived(new MockDataStoreProcessor());
// The application will function otherwise as normal,
// returning a response back to the caller
}
@State("another state from the pact contract")
@Test
public void anotherTest() throws Exception {
// ...
}
class MockDataStoreProcessor implements Processor {
@Override
public void process(Exchange exchange) throws Exception {
exchange.getMessage().setBody("some fake data");
}
}
}
Finally configure the Maven Failsafe Plugin to run the test with specific properties to connect to the Pact Broker:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<!-- Configure plugin to play nicely with Spring Boot -->
<!-- https://github.com/spring-projects/spring-boot/issues/6254#issuecomment-307151464 -->
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
<!-- Configure PACT to publish contract test results when finished -->
<systemPropertyVariables>
<pactbroker.host>my-pact-broker</pactbroker.host>
<pactbroker.port>8080</pactbroker.port>
<pactbroker.username>admin</pactbroker.username>
<pactbroker.password>letmein</pactbroker.password>
<pact.provider.version>${project.version}</pact.provider.version>
</systemPropertyVariables>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
Then the test can be run using:
mvn clean verify