This is an example project demonstrating how to test-drive a new endpoint on a Spring Boot provider, starting from the consumer side.
Like a real project, commits in this repository incrementally add tests and implementation. Use a tool of your choice to view the diffs between each commit.
Some of the commits have failing tests to highlight intermediate steps and to show how failing tests inform us about the next steps.
The provider test is implemented as an integration test, running the whole
application in the pact
Spring profile. In a real app, this profile should
stub out external dependencies to prevent side effects and to make the test
predictable and robust.
Running contract tests as integration tests is definitely controversial. In my experience, I have seen more up- than downsides to this approach: it's usually not more complicated to set up preconditions in the database vs. setting up mocks. The tests are also not brittle if the application is designed to be testable and if its output is reproducible (e.g. list item order needs to be stable). On the plus side, this approach allows having an integration-like test for every response case of each endpoint. And who would say no to free integration tests?
Prep work:
- Run the Pact Broker locally through
docker compose up -d
in the Broker repo and delete any existing pacts. - Remove any pacts inside the
pacts
folder in the consumer.
Presentation:
- Go through some slides explaining the pains of working without consumer-driver contract tests.
- Run mvn spring-boot:run for both projects and watch the console log of the consumer.
- In the provider, change the
name
property inAnimalResponse
totype
, and watch the consumer log. - Go through some more slides explaining consumer-driven contract testing conceptually.
- Explore the tests in the consumer.
- Run the
listAnimals_returnsAnimals
test and verify the creation of the new pact file. - Explore the tests in the provider.
- Run the
PactIntegrationTest
and inspect the error returned from it. - Change
type
back toname
and rerun the test. - Look at the
@PactFolder
annotation, and notice the dependency on the consumer codebase. - Go to the Pact Broker UI and observe there are no contracts.
- Publish to the Pact Broker by running the
pact:publish
consumer maven goal. - In the Broker, take a look at the newly published contract.
- Run the
pact:can-i-deploy
consumer maven goal to verify if we can deploy the consumer. - In the Broker, note that the new contract has not been verified yet.
- Change the
@PactFolder("../pact-consumer/pacts")
annotation to@PactBroker(host = "localhost", port = "80")
- Run the
Test and publish results
provider maven goal to report the verification results to the Pact Broker. This goal is actually just themvn test -Dpact.verifier.publishResults=true -f pom.xml
command. - Run the
pact:can-i-deploy
maven goal to verify if we can deploy the provider. - Run the
pact:can-i-deploy
maven goal to verify if we can deploy the consumer. - Explore the
ci-script.sh
files in both applications and understand the versioning. - Run the consumer
ci-script.sh
file and look at the matrix in the Pact Broker. - Run the provider
ci-script.sh
file and look at the matrix in the Pact Broker. - Rerun the consumer
ci-script.sh
file now that the provider is deployed to the test environment. - Explain the idea of versions and tagging.
- Go through some more slides.
More to discover:
- Feature branches (https://github.com/pact-foundation/pact-jvm/tree/master/provider/maven#publishing-pact-files-to-a-pact-broker)
- Pending / WIP pacts (https://docs.pact.io/pact_broker/advanced_topics/pending_pacts/ https://docs.pact.io/implementation_guides/jvm/provider/junit/#pending-pact-support-version-413-and-later)