
In the following article we are going to discuss about how to integrate pact dependencies and create 1 consumer test and the coresponding producer verification.
We will have two service that interact each other using API calls :
- consumer bonus-consumer
- producer bonus-service

While using Pact, the consumer is defining the data model of the contract and the provider is compling with the data.

Our consumer service bonus-consumer is using the getAllBonuses API from the provider and he wants the data to be given as the above contract.
Consumer part
<dependency>
<groupId>au.com.dius.pact.consumer</groupId>
<artifactId>junit5</artifactId>
<version>4.3.9</version>
</dependency>
<plugin>
<groupId>au.com.dius.pact.provider</groupId>
<artifactId>maven</artifactId>
<version>4.3.9</version>
<configuration>
<pactBrokerUrl>http://localhost:8000/</pactBrokerUrl>
<pactBrokerUsername>pact_workshop</pactBrokerUsername>
<pactBrokerPassword>pact_workshop</pactBrokerPassword>
<tags>getGitBranch(), 'test', 'prod'</tags>
</configuration>
</plugin>
We will use the plugin to push the generated contrat to the Pact broker that will be started using a docker compose file.
Writing the consumer test
Class level annotation
@SpringBootTest
@ExtendWith(PactConsumerTestExt.class)
@PactTestFor(providerName = "bonus-service")
@Slf4j
public class BonusPactTest {
All of the contracts and tests from this class will relate to the provider added into the providerName. Slf4j is not mandatory.
Method that defines the contract
@Pact(consumer = "bonus-consumer")
public V4Pact getBonus(PactDslWithProvider builder) {
DslPart bonusResponse = PactDslJsonArray.arrayEachLike()
.integerType("id", 1)
.stringType("bonusName", "SUPER")
.stringType("bonusType", "MEGA_BONUS","SUPER_BONUS","EXTRA_BONUS")
.stringMatcher("startTime", "^\\d{4}\\-(0?[1-9]|1[012])\\-(0?[1-9]|[12][0-9]|3[01])$","2019-10-22")
.stringMatcher("endTime","^\\d{4}\\-(0?[1-9]|1[012])\\-(0?[1-9]|[12][0-9]|3[01])$", "2019-10-30")
.closeObject();
return builder.given("I get all bonuses")
.uponReceiving("Get bonus call")
.path("/bonus/getAllBonuses")
.method("GET")
.willRespondWith()
.status(200)
.body(bonusResponse)
.toPact(V4Pact.class);
}
The regex used above will make sure that the date specified will be in the yyyy-mm-dd format. How let’s create the test for the contract above.
Consumer test creation
@Test
@PactTestFor(pactMethod = "getBonus", port = "8081")
public void testGetBonuses(MockServer mockServer) throws JSONException {
RestTemplate restTemplate = new RestTemplateBuilder().rootUri(mockServer.getUrl()).build();
ResponseEntity<String> response = restTemplate.getForEntity("/bonus/getAllBonuses", String.class);
log.info("Response body contains{}", response.getBody());
JSONArray array = new JSONArray(response.getBody());
JSONObject bonus = new JSONObject(array.get(0).toString());
int statusCode = response.getStatusCode().value();
Assertions.assertEquals(statusCode, 200);
Assertions.assertEquals(bonus.get("bonusType"), "MEGA_BONUS");
}
The method above tels that this test is for the pact method specified at pactMethod parameter, starts a mock http server on port 8081 with one GET interaction and response that we specified.
Run the test and check the generated contract json file

The default path where the contract are generated is target/pacts folder

Start pact broker and push the contract
In the root path of the bonus-consumer we have a docker compose file already configured with the broker and a database to keep the interactins.
Open a terminal in the location of the compose file and run :
- docker compose up
A pact broker server will start on http://localhost:8000

- Now let’s publish the consumer contract.
Open a terminal in the root path of the service and run mvn pact:publish


Now that the contract was pushed to the broker, let’s move to the provider test.
Provider part
<dependency>
<groupId>au.com.dius.pact.provider</groupId>
<artifactId>junit5spring</artifactId>
<version>4.3.9</version>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<systemPropertyVariables>
<pact.verifier.publishResults>true</pact.verifier.publishResults>
<pact.provider.version>${project.version}</pact.provider.version>
</systemPropertyVariables>
<includes>
<include>VerifyContractTest.java</include>
</includes>
</configuration>
</plugin>
We will use the first dependency to write the provider integration and with the surefire plugin we will run the tests with mvn clean install and publish the interaction with the consumer.
Writing the provider test
Class level annotations
@VerificationReports
@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,classes = TestConfiguration.class)
@Provider(value = "bonus-service")
//@PactFolder("pacts")
@Slf4j
@PactBroker
public class VerifyContractTest {
We define the provider with @Provider and configure the @PactBroker using the application.properties
pactbroker.url=http://localhost:8000/ #Used when broker has authentication setup #pactbroker.auth.username=pact_workshop #pactbroker.auth.password=pact_workshop
@MockBean
BonusController bonusController;
@TestTarget
public MockMvcTestTarget testTarget = new MockMvcTestTarget();
@Autowired
MappingJackson2HttpMessageConverter converter;
On top of the class we need to create a mock bean of the controller under test ,define the test target and converters for our data.
@BeforeEach
void setup(PactVerificationContext context){
testTarget.setControllers(bonusController);
testTarget.setMessageConverters(converter);
testTarget.setPrintRequestResponse(true);
context.setTarget(testTarget);
//System.setProperty("pact.verifier.publishResults", "true");
}
@TestTemplate
@ExtendWith(PactVerificationSpringProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context,
MockHttpServletRequestBuilder request) {
request.header("Content-Type", "application/json");
context.verifyInteraction();
}
Before each test we configure the Mvc test target and we add that into the Pact context.
Uncommenting the code with the system property will also publish interaction result to the broker for all of the class states that we specify.
@State("I get all bonuses")
void getAllBonuses() throws IOException {
// Method1 using a builder
Bonus bonus = Bonus.builder()
.id(1)
.bonusName("MEGA")
.bonusType("MEGA_BONUS")
.startTime(Date.valueOf("2019-10-10"))
.endTime(Date.valueOf("2019-10-10")).build();
// Method2 Reading from json file
File myRequest = ResourceUtils.getFile("classpath:requests/bonusRequest.json");
Bonus bonusRequest = new ObjectMapper().readValue(myRequest, Bonus.class);
log.info("Bonus is {}", bonusRequest.toString());
BDDMockito.doReturn(List.of(bonus)).when(bonusController).getBonuses();
}
This is the state name that the bonus-client defined in his contract.
There are multiple ways of running the provider test, offline by using the copied contract from consumer, from the class level and we can also run it from the command line with publish and non publishing results.
A. Running the test using contract copied from consumer (publish interaction disabled)
Disable @PactBroker and enable @PactFolder will run the test by using the local json contract that we copied into the pacts folder

B. Running the test using the pact broker (enable @PactBroker and disable @PactFolder – publish interaction disabled)

C. Running the test using the maven command and publish verification > mvn clean install (publish interaction enabled)

Verify the pact broker for results


Project location for both producer and consumer can be found here :
- Consumer – https://github.com/diaconur/bonus-service/tree/bonus-client-pact
- Provider – https://github.com/diaconur/bonus-service/tree/bonus-pact-provider
Have fun and keep learning








Leave a comment