Consider Consumer-Driven Contracts as a Design Pattern.
Problem statement:
How can a web service API reflect its clients' needs
while enabling evolution
and avoiding breaking clients?
[1] Ian Robinson, Consumer-Driven Contracts, Service Design Patterns
How can we prevent breaking changes to services?
[1] Ian Robinson, Consumer-Driven Contracts, Service Design Patterns
The set of integration tests received from all existing consumers represents the service's obligations to its consumer base.
[1] Ian Robinson, Consumer-Driven Contracts, Service Design Patterns
Contracts codify the result of conversations - they don't replace them.
Consumer-Driven Contracts don't make sense everywhere.
They should be used when:
[1] Ian Robinson, Consumer-Driven Contracts, Service Design Patterns
"The similarly named Pact and Pacto are two new open-source tools which allow testing interactions between service providers and consumers in isolation against a contract." [1]
Service consumers define the requests they'll make and the responses they expect back. These expectations are used to run consumer tests against a mock service provider, recording interactions to a pact file.
Recorded interactions with the consumer are played back in the service provider tests to ensure the service provider actually does provide the response the consumer expects.
Spring Boot Microservices + Pact-JVM
git clone git@github.com:brookingcharlie/microservices-pact.git
# Build/test the consumer
./gradlew microservices-pact-consumer:test
less microservices-pact-consumer/target/pacts/Foo_Consumer-Foo_Provider.json
# Build/test the provider
./gradlew microservices-pact-provider:assemble
./gradlew microservices-pact-provider:pactVerify
# Run the provider
java -jar microservices-pact-provider/build/libs/microservices-pact-provider-0.0.1.jar
# ... then in another window
curl -v 'http://localhost:8080/foos/'
--- a/microservices-pact-provider/src/main/java/io/pivotal/microservices/pact/provider/Application.java
+++ b/microservices-pact-provider/src/main/java/io/pivotal/microservices/pact/provider/Application.java
@@ -26,4 +26,4 @@ public class Application {
- @RequestMapping(value = "/foos", method = RequestMethod.GET)
+ @RequestMapping(value = "/foos", method = RequestMethod.GET, produces = "application/json;charset=ASCII")
public ResponseEntity> foos() {
return new ResponseEntity<>(Arrays.asList(new Foo(42), new Foo(100)), HttpStatus.OK);
}
--- a/microservices-pact-provider/src/main/java/io/pivotal/microservices/pact/provider/Foo.java
+++ b/microservices-pact-provider/src/main/java/io/pivotal/microservices/pact/provider/Foo.java
@@ -11,8 +11,8 @@ public class Foo {
- public int getValue() {
+ public int getVal() {
return value;
}
- public void setValue(int value) {
+ public void setVal(int value) {
this.value = value;
}
}
patch -p1 -i break-provider.patch
./gradlew microservices-pact-provider:assemble microservices-pact-provider:pactVerify
--- a/microservices-pact-consumer/src/main/java/io/pivotal/microservices/pact/consumer/ConsumerPort.java
+++ b/microservices-pact-consumer/src/main/java/io/pivotal/microservices/pact/consumer/ConsumerPort.java
@@ -23,6 +23,6 @@ public class ConsumerPort {
public List foos() {
ParameterizedTypeReference> responseType = new ParameterizedTypeReference>() {};
- return restTemplate.exchange(url + "/foos", HttpMethod.GET, null, responseType).getBody();
+ return restTemplate.exchange(url + "/foos", HttpMethod.POST, null, responseType).getBody();
}
}
--- a/microservices-pact-consumer/src/test/java/io/pivotal/microservices/pact/consumer/ConsumerPortTest.java
+++ b/microservices-pact-consumer/src/test/java/io/pivotal/microservices/pact/consumer/ConsumerPortTest.java
@@ -23,6 +23,6 @@ public class ConsumerPortTest {
return builder.uponReceiving("a request for Foos")
.path("/foos")
- .method("GET")
+ .method("POST")
.willRespondWith()
.headers(headers)
patch -p1 -i break-consumer.patch
./gradlew microservices-pact-consumer:test microservices-pact-provider:pactVerify
--- a/microservices-pact-provider/src/main/java/io/pivotal/microservices/pact/provider/Foo.java
+++ b/microservices-pact-provider/src/main/java/io/pivotal/microservices/pact/provider/Foo.java
@@ -15,4 +15,8 @@ public class Foo {
public void setValue(int value) {
this.value = value;
}
+
+ public String getExtra() {
+ return "123";
+ }
}
patch -p1 -i evolve-provider.patch
./gradlew microservices-pact-provider:assemble microservices-pact-provider:pactVerify