SlideShare a Scribd company logo
The case for consumer-driven contracts
@matthewfellows
Picture the scene...
Monday morning
9am
Phone rings
It’s our VC
We just got the news....
The case for consumer-driven contracts
We’re going to build a startup!
The case for consumer-driven contracts
Soundify
®
Everyone is doing microservices!
The case for consumer-driven contracts
Metric Value
No. teams 1
No. components 1
Test environments 1
Time in pipeline (commit to prod) 5 minutes
Risk 2.5%
Deployments per day 10
CD health check
The case for consumer-driven contracts
Metric Value
No. teams 2
No. components 2
Test environments 4
Time in pipeline (commit to prod) 10 minutes
Risk 5%
Deployments per day 10
CD health check
The case for consumer-driven contracts
The case for consumer-driven contracts
Metric Value
No. teams 3
No. components 6
Test environments 18
Time in pipeline (commit to prod) 30 minutes
Risk 10%
Deployments per day 10
CD health check
ACQUIRED
The case for consumer-driven contracts
Metric Value
No. teams 10
No. components 20
Test environments 20 200
Time in pipeline (commit to prod) 120+ minutes
Risk 20%
Deployments per day 1
CD health check
So what did we learn?
The case for consumer-driven contracts
Where did we go wrong?
We pretended microservices were colocated libraries
Cohesive services, loosely coupled
vs
Cohesive teams, tightly coupled by services
Current tooling and strategies are not good enough
“Integration tests are a scam”
- JB Rainsberger
Scam, you say? Justify!
Integrated tests are:
● Slow
● Fragile
● Hard to manage
When they fail, you can’t point to the problem!
Scam, you say? Justify!
“But my integration tests run in Docker,
why can’t I use them?”
- People
Scam, you say? Justify!
“Because Maths”
- Me
The case for consumer-driven contracts
Branches per box vs test cases required
2 code branches = 128 tests
5 code branches = 78,125 tests
10 code branches = 10M tests
Good tests have the exact opposite properties
The case for consumer-driven contracts
Dictator Driven Contracts
Dictator Driven Contracts
1. Sit in ivory tower and postulate
2. Document perfect API (Swagger, API blueprint etc.)
3. Create said API
4. Publish said document to consumers
5. Request dictate consumers update
6. Repeat steps 1-5
How to: Dictator Driven Contracts
The case for consumer-driven contracts
The case for consumer-driven contracts
The case for consumer-driven contracts
Crap, this didn’t work either!
Dictator Consumer Driven Contracts
The case for consumer-driven contracts
Benefits?
You’ll know when you break a consumer
You have a form of documentation
You can test things independently
Pact
www.pact.io
Evolved from combining these two principles
Step 1: Define Consumer expectations
Step 1: Define Consumer expectations
Step 1: Define Consumer expectations
Step 1: Define Consumer expectations
Step 2: Verify expectations on Provider
Start with a consumer test
Given “User A exists”
When I Receive “a GET request for user A”
With “these headers and query”
Respond with “200 OK”
And “User A’s details in the body”
Given “User A does not exist”
When I Receive “a GET request for user A”
Respond with “404 Not Found”
Example
// Setup our expected interactions on the Mock Service.
pact.
AddInteraction().
Given("User billy exists").
UponReceiving("A request to login with user 'billy'").
WithRequest(dsl.Request{
Method: "POST",
Path: "/users/login",
Body: loginRequest,
}).
WillRespondWith(dsl.Response{
Status: 200,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: `
{
"user": {
"name": "billy"
}
}
`,
})
// Run the test and verify the interactions.
err := pact.Verify(func() error {
client := Client{
Host: fmt.Sprintf("http://localhost:%d", pact.Server.Port),
}
client.loginHandler(rr, req)
// Expect User to be set on the Client
if client.user == nil {
return errors.New("Expected user not to be nil")
}
return nil
})
if err != nil {
t.Fatalf("Error on Verify: %v", err)
}
// Write pact to file `<pwd>/pacts/my_consumer-my_provider.json`
// NOTE: This also is a good candidate for use in TestMain(m *testing.M)
pact.WritePact()
Specification by example
{
"consumer": {"name": "MyConsumer"},
"provider": {"name": "MyProvider"},
"interactions": [
{
"description": "Some name for the test",
"provider_state": "Some state",
"request": {
"method": "GET",
"path": "/foobar",
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "foo"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "bar"
}
}
}
...
]
{
"consumer": {"name": "MyConsumer"},
"provider": {"name": "MyProvider"},
"interactions": [
{
"description": "Some name for the test",
"provider_state": "Some state",
"request": {
"method": "GET",
"path": "/foobar",
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "foo"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "bar"
}
}
}
...
]
{
"consumer": {"name": "MyConsumer"},
"provider": {"name": "MyProvider"},
"interactions": [
{
"description": "Some name for the test",
"provider_state": "Some state",
"request": {
"method": "GET",
"path": "/foobar",
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "foo"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "bar"
}
}
}
...
]
{
"consumer": {"name": "MyConsumer"},
"provider": {"name": "MyProvider"},
"interactions": [
{
"description": "Some name for the test",
"provider_state": "Some state",
"request": {
"method": "GET",
"path": "/foobar",
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "foo"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "bar"
}
}
}
...
]
{
"consumer": {"name": "MyConsumer"},
"provider": {"name": "MyProvider"},
"interactions": [
{
"description": "Some name for the test",
"provider_state": "Some state",
"request": {
"method": "GET",
"path": "/foobar",
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "foo"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"s": "bar"
}
}
}
...
]
Next publish your pacts
The case for consumer-driven contracts
The case for consumer-driven contracts
The case for consumer-driven contracts
// Publish the Pacts...
p := dsl.Publisher{}
err := p.Publish(types.PublishRequest{
PactURLs: []string{"../pacts/myconsumer-myprovider.json"},
PactBroker: os.Getenv("PACT_BROKER_HOST"),
ConsumerVersion: "1.0.0",
Tags: []string{"latest", "production"},
BrokerUsername: os.Getenv("PACT_BROKER_USERNAME"),
BrokerPassword: os.Getenv("PACT_BROKER_PASSWORD"),
})
Then verify your provider
The case for consumer-driven contracts
// Verify the Provider from tagged Pact files stored in a Pact Broker
response = pact.VerifyProvider(types.VerifyRequest{
ProviderBaseURL: fmt.Sprintf("http://localhost:%d", providerPort),
BrokerURL: brokerHost,
Tags: []string{"latest", "prod"},
ProviderStatesURL: fmt.Sprintf("http://localhost:%d/states", providerPort),
ProviderStatesSetupURL: fmt.Sprintf("http://localhost:%d/setup", providerPort),
BrokerUsername: os.Getenv("PACT_BROKER_USERNAME"),
BrokerPassword: os.Getenv("PACT_BROKER_PASSWORD"),
})
if response.ExitCode != 0 {
t.Fatalf("Got %d, Want exit code 0", response.ExitCode)
}
Verifying a pact between billy and bobby
Given User billy exists
A request to login with user 'billy'
with POST /users/login
returns a response which
has status code 200
has a matching body
includes headers
"Content-Type" with value "application/json"
Given User billy does not exist
A request to login with user 'billy'
with POST /users/login
returns a response which
has status code 404
includes headers
"Content-Type" with value "application/json"
...
Finished in 0.03042 seconds
7 examples, 0 failures
Verifying a pact between billy and bobby
Given User billy exists
A request to login with user 'billy'
with POST /users/login
returns a response which
has status code 200
has a matching body
includes headers
"Content-Type" with value "application/json"
Given User billy does not exist
A request to login with user 'billy'
with POST /users/login
returns a response which
has status code 404
includes headers
"Content-Type" with value "application/json"
...
Finished in 0.03042 seconds
7 examples, 0 failures
Verifying a pact between billy and bobby
Given User billy exists
A request to login with user 'billy'
with POST /users/login
returns a response which
has status code 200
has a matching body
includes headers
"Content-Type" with value "application/json"
Given User billy does not exist
A request to login with user 'billy'
with POST /users/login
returns a response which
has status code 404
includes headers
"Content-Type" with value "application/json"
...
Finished in 0.03042 seconds
7 examples, 0 failures
Verifying a pact between billy and bobby
Given User billy exists
A request to login with user 'billy'
with POST /users/login
returns a response which
has status code 200
has a matching body (FAILED - 1)
includes headers
"Content-Type" with value "application/json"
Failures:
1) Verifying a pact between billy and bobby Given User billy exists A request to login with user 'billy' with POST
/users/login returns a response which has a matching body
Failure/Error: expect(response_body).to match_term expected_response_body, diff_options
Actual: {"user":{"user":"billy"}}
@@ -1,6 +1,5 @@
{
"user": {
- "name": "billy"
}
}
No Silver Bullet
Does not replace communication
The case for consumer-driven contracts
What about systems maintained by other teams?
What about systems built in outdated technologies?
Scary outside
world!
3rd Party
Mainframe
Recapping...
● Business impact of integrated tests
● Cause and explanation of those effects
● Alternative approach - isolation + contracts
● Contract-testing as an approach, Pact as a tool (in this order!)
Thank you
- @matthewfellows
Given “The presentation is over”
Upon Receiving “A request for an answer”
With “A valid question”
Respond With “A valid answer”

More Related Content

Similar to The case for consumer-driven contracts (20)

PDF
CDC Tests - Integration Tests cant be made simpler than this!
Ramya Authappan
 
PPTX
Consumer Driven Contracts - A Deep Dive
Ramya Authappan
 
PDF
Move fast and consumer driven contract test things
Alon Pe'er
 
PPTX
"Asynchronous" Integration Tests for Microservices - RootConf 2017
Ramya Authappan
 
PPTX
Consumer-driven contracts with Pact and PHP
Andy Kelk
 
PDF
Consumer Driven Contracts (DDD Perth 2016)
Rob Crowley
 
PDF
Dropwizard with MongoDB and Google Cloud
Yun Zhi Lin
 
PDF
Collaborating with Contracts
stania_ang
 
PDF
Test driven development with behat and silex
Dionyshs Tsoumas
 
PPTX
Agile methodologies based on BDD and CI by Nikolai Shevchenko
Moldova ICT Summit
 
PDF
RefCard RESTful API Design
OCTO Technology
 
PPTX
ATAGTR2017 CDC Tests - Integration Tests cant be made simpler than this!
Agile Testing Alliance
 
PDF
MongoDB Mobile
MongoDB
 
PDF
Cloud Foundry Cookbook: Recipes for a Successful Cloud Foundry Deployment in ...
VMware Tanzu
 
PDF
Recipes for a successful production cloudfoundry deployment - CF Summit 2014
Vinícius Carvalho
 
PDF
Distributed and Cloud Computing 1st Edition Hwang Solutions Manual
rewidasateu
 
DOCX
Api testing bible using postman
Abhishek Saxena
 
PDF
How DUO started with Continuous Delivery and changed their way of Testing
Xebia Nederland BV
 
PPTX
Bdd with Cucumber and Mocha
Atish Narlawar
 
CDC Tests - Integration Tests cant be made simpler than this!
Ramya Authappan
 
Consumer Driven Contracts - A Deep Dive
Ramya Authappan
 
Move fast and consumer driven contract test things
Alon Pe'er
 
"Asynchronous" Integration Tests for Microservices - RootConf 2017
Ramya Authappan
 
Consumer-driven contracts with Pact and PHP
Andy Kelk
 
Consumer Driven Contracts (DDD Perth 2016)
Rob Crowley
 
Dropwizard with MongoDB and Google Cloud
Yun Zhi Lin
 
Collaborating with Contracts
stania_ang
 
Test driven development with behat and silex
Dionyshs Tsoumas
 
Agile methodologies based on BDD and CI by Nikolai Shevchenko
Moldova ICT Summit
 
RefCard RESTful API Design
OCTO Technology
 
ATAGTR2017 CDC Tests - Integration Tests cant be made simpler than this!
Agile Testing Alliance
 
MongoDB Mobile
MongoDB
 
Cloud Foundry Cookbook: Recipes for a Successful Cloud Foundry Deployment in ...
VMware Tanzu
 
Recipes for a successful production cloudfoundry deployment - CF Summit 2014
Vinícius Carvalho
 
Distributed and Cloud Computing 1st Edition Hwang Solutions Manual
rewidasateu
 
Api testing bible using postman
Abhishek Saxena
 
How DUO started with Continuous Delivery and changed their way of Testing
Xebia Nederland BV
 
Bdd with Cucumber and Mocha
Atish Narlawar
 

More from DiUS (20)

PDF
Lunch and Learn: You have the data, now what?
DiUS
 
PDF
How to build confidence in your release cycle
DiUS
 
PDF
Serverless microservices: Test smarter, not harder
DiUS
 
PDF
Test Smart, not hard
DiUS
 
PDF
10 things-to-inspire-in-10-mins
DiUS
 
PDF
Trends and development practices in Serverless architectures
DiUS
 
PDF
Deploying large-scale, serverless and asynchronous systems - without integrat...
DiUS
 
PDF
The Diversity Dilemma - Supporting our Sisters in STEM
DiUS
 
PDF
GameDay - Achieving resilience through Chaos Engineering
DiUS
 
PDF
Crafting Quality Software
DiUS
 
PPTX
Metrics on the front, data in the back
DiUS
 
PPTX
Antifragility and testing for distributed systems failure
DiUS
 
PDF
DIY IoT Backend
DiUS
 
PDF
How to Build Hardware Lean
DiUS
 
PDF
Behaviour Change and Coaching: What we can learn from BJ Fogg
DiUS
 
PDF
Power in Agile Teams
DiUS
 
PPTX
The Diversity Dilemma: Attracting and Retaining Talented Women in Technology-...
DiUS
 
PDF
Rise of the machines: Continuous Delivery at SEEK - YOW! Night Summary Slides
DiUS
 
PPTX
AWS Summit Melbourne 2014 | The Path to Business Agility for Vodafone: How Am...
DiUS
 
PPTX
Agile Australia 2014 | A light saber for your disruptive tool belt: the Busin...
DiUS
 
Lunch and Learn: You have the data, now what?
DiUS
 
How to build confidence in your release cycle
DiUS
 
Serverless microservices: Test smarter, not harder
DiUS
 
Test Smart, not hard
DiUS
 
10 things-to-inspire-in-10-mins
DiUS
 
Trends and development practices in Serverless architectures
DiUS
 
Deploying large-scale, serverless and asynchronous systems - without integrat...
DiUS
 
The Diversity Dilemma - Supporting our Sisters in STEM
DiUS
 
GameDay - Achieving resilience through Chaos Engineering
DiUS
 
Crafting Quality Software
DiUS
 
Metrics on the front, data in the back
DiUS
 
Antifragility and testing for distributed systems failure
DiUS
 
DIY IoT Backend
DiUS
 
How to Build Hardware Lean
DiUS
 
Behaviour Change and Coaching: What we can learn from BJ Fogg
DiUS
 
Power in Agile Teams
DiUS
 
The Diversity Dilemma: Attracting and Retaining Talented Women in Technology-...
DiUS
 
Rise of the machines: Continuous Delivery at SEEK - YOW! Night Summary Slides
DiUS
 
AWS Summit Melbourne 2014 | The Path to Business Agility for Vodafone: How Am...
DiUS
 
Agile Australia 2014 | A light saber for your disruptive tool belt: the Busin...
DiUS
 
Ad

Recently uploaded (20)

PPTX
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
PDF
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
PDF
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
PPTX
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
PDF
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
PDF
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
PDF
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PPTX
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
PPTX
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
PPTX
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
PDF
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PDF
July Patch Tuesday
Ivanti
 
PDF
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
DOCX
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
July Patch Tuesday
Ivanti
 
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
Ad

The case for consumer-driven contracts

  • 1. The case for consumer-driven contracts @matthewfellows
  • 4. 9am
  • 7. We just got the news....
  • 9. We’re going to build a startup!
  • 12. Everyone is doing microservices!
  • 14. Metric Value No. teams 1 No. components 1 Test environments 1 Time in pipeline (commit to prod) 5 minutes Risk 2.5% Deployments per day 10 CD health check
  • 16. Metric Value No. teams 2 No. components 2 Test environments 4 Time in pipeline (commit to prod) 10 minutes Risk 5% Deployments per day 10 CD health check
  • 19. Metric Value No. teams 3 No. components 6 Test environments 18 Time in pipeline (commit to prod) 30 minutes Risk 10% Deployments per day 10 CD health check
  • 22. Metric Value No. teams 10 No. components 20 Test environments 20 200 Time in pipeline (commit to prod) 120+ minutes Risk 20% Deployments per day 1 CD health check
  • 23. So what did we learn?
  • 25. Where did we go wrong?
  • 26. We pretended microservices were colocated libraries
  • 27. Cohesive services, loosely coupled vs Cohesive teams, tightly coupled by services
  • 28. Current tooling and strategies are not good enough
  • 29. “Integration tests are a scam” - JB Rainsberger
  • 30. Scam, you say? Justify! Integrated tests are: ● Slow ● Fragile ● Hard to manage When they fail, you can’t point to the problem!
  • 31. Scam, you say? Justify! “But my integration tests run in Docker, why can’t I use them?” - People
  • 32. Scam, you say? Justify! “Because Maths” - Me
  • 34. Branches per box vs test cases required 2 code branches = 128 tests 5 code branches = 78,125 tests 10 code branches = 10M tests
  • 35. Good tests have the exact opposite properties
  • 39. 1. Sit in ivory tower and postulate 2. Document perfect API (Swagger, API blueprint etc.) 3. Create said API 4. Publish said document to consumers 5. Request dictate consumers update 6. Repeat steps 1-5 How to: Dictator Driven Contracts
  • 43. Crap, this didn’t work either!
  • 47. You’ll know when you break a consumer
  • 48. You have a form of documentation
  • 49. You can test things independently
  • 51. Evolved from combining these two principles
  • 52. Step 1: Define Consumer expectations
  • 53. Step 1: Define Consumer expectations
  • 54. Step 1: Define Consumer expectations
  • 55. Step 1: Define Consumer expectations Step 2: Verify expectations on Provider
  • 56. Start with a consumer test
  • 57. Given “User A exists” When I Receive “a GET request for user A” With “these headers and query” Respond with “200 OK” And “User A’s details in the body”
  • 58. Given “User A does not exist” When I Receive “a GET request for user A” Respond with “404 Not Found”
  • 60. // Setup our expected interactions on the Mock Service. pact. AddInteraction(). Given("User billy exists"). UponReceiving("A request to login with user 'billy'"). WithRequest(dsl.Request{ Method: "POST", Path: "/users/login", Body: loginRequest, }). WillRespondWith(dsl.Response{ Status: 200, Headers: map[string]string{ "Content-Type": "application/json", }, Body: ` { "user": { "name": "billy" } } `, })
  • 61. // Run the test and verify the interactions. err := pact.Verify(func() error { client := Client{ Host: fmt.Sprintf("http://localhost:%d", pact.Server.Port), } client.loginHandler(rr, req) // Expect User to be set on the Client if client.user == nil { return errors.New("Expected user not to be nil") } return nil }) if err != nil { t.Fatalf("Error on Verify: %v", err) } // Write pact to file `<pwd>/pacts/my_consumer-my_provider.json` // NOTE: This also is a good candidate for use in TestMain(m *testing.M) pact.WritePact()
  • 63. { "consumer": {"name": "MyConsumer"}, "provider": {"name": "MyProvider"}, "interactions": [ { "description": "Some name for the test", "provider_state": "Some state", "request": { "method": "GET", "path": "/foobar", "headers": { "Content-Type": "application/json" }, "body": { "s": "foo" } }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "body": { "s": "bar" } } } ... ]
  • 64. { "consumer": {"name": "MyConsumer"}, "provider": {"name": "MyProvider"}, "interactions": [ { "description": "Some name for the test", "provider_state": "Some state", "request": { "method": "GET", "path": "/foobar", "headers": { "Content-Type": "application/json" }, "body": { "s": "foo" } }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "body": { "s": "bar" } } } ... ]
  • 65. { "consumer": {"name": "MyConsumer"}, "provider": {"name": "MyProvider"}, "interactions": [ { "description": "Some name for the test", "provider_state": "Some state", "request": { "method": "GET", "path": "/foobar", "headers": { "Content-Type": "application/json" }, "body": { "s": "foo" } }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "body": { "s": "bar" } } } ... ]
  • 66. { "consumer": {"name": "MyConsumer"}, "provider": {"name": "MyProvider"}, "interactions": [ { "description": "Some name for the test", "provider_state": "Some state", "request": { "method": "GET", "path": "/foobar", "headers": { "Content-Type": "application/json" }, "body": { "s": "foo" } }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "body": { "s": "bar" } } } ... ]
  • 67. { "consumer": {"name": "MyConsumer"}, "provider": {"name": "MyProvider"}, "interactions": [ { "description": "Some name for the test", "provider_state": "Some state", "request": { "method": "GET", "path": "/foobar", "headers": { "Content-Type": "application/json" }, "body": { "s": "foo" } }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "body": { "s": "bar" } } } ... ]
  • 72. // Publish the Pacts... p := dsl.Publisher{} err := p.Publish(types.PublishRequest{ PactURLs: []string{"../pacts/myconsumer-myprovider.json"}, PactBroker: os.Getenv("PACT_BROKER_HOST"), ConsumerVersion: "1.0.0", Tags: []string{"latest", "production"}, BrokerUsername: os.Getenv("PACT_BROKER_USERNAME"), BrokerPassword: os.Getenv("PACT_BROKER_PASSWORD"), })
  • 73. Then verify your provider
  • 75. // Verify the Provider from tagged Pact files stored in a Pact Broker response = pact.VerifyProvider(types.VerifyRequest{ ProviderBaseURL: fmt.Sprintf("http://localhost:%d", providerPort), BrokerURL: brokerHost, Tags: []string{"latest", "prod"}, ProviderStatesURL: fmt.Sprintf("http://localhost:%d/states", providerPort), ProviderStatesSetupURL: fmt.Sprintf("http://localhost:%d/setup", providerPort), BrokerUsername: os.Getenv("PACT_BROKER_USERNAME"), BrokerPassword: os.Getenv("PACT_BROKER_PASSWORD"), }) if response.ExitCode != 0 { t.Fatalf("Got %d, Want exit code 0", response.ExitCode) }
  • 76. Verifying a pact between billy and bobby Given User billy exists A request to login with user 'billy' with POST /users/login returns a response which has status code 200 has a matching body includes headers "Content-Type" with value "application/json" Given User billy does not exist A request to login with user 'billy' with POST /users/login returns a response which has status code 404 includes headers "Content-Type" with value "application/json" ... Finished in 0.03042 seconds 7 examples, 0 failures
  • 77. Verifying a pact between billy and bobby Given User billy exists A request to login with user 'billy' with POST /users/login returns a response which has status code 200 has a matching body includes headers "Content-Type" with value "application/json" Given User billy does not exist A request to login with user 'billy' with POST /users/login returns a response which has status code 404 includes headers "Content-Type" with value "application/json" ... Finished in 0.03042 seconds 7 examples, 0 failures
  • 78. Verifying a pact between billy and bobby Given User billy exists A request to login with user 'billy' with POST /users/login returns a response which has status code 200 has a matching body includes headers "Content-Type" with value "application/json" Given User billy does not exist A request to login with user 'billy' with POST /users/login returns a response which has status code 404 includes headers "Content-Type" with value "application/json" ... Finished in 0.03042 seconds 7 examples, 0 failures
  • 79. Verifying a pact between billy and bobby Given User billy exists A request to login with user 'billy' with POST /users/login returns a response which has status code 200 has a matching body (FAILED - 1) includes headers "Content-Type" with value "application/json" Failures: 1) Verifying a pact between billy and bobby Given User billy exists A request to login with user 'billy' with POST /users/login returns a response which has a matching body Failure/Error: expect(response_body).to match_term expected_response_body, diff_options Actual: {"user":{"user":"billy"}} @@ -1,6 +1,5 @@ { "user": { - "name": "billy" } }
  • 81. Does not replace communication
  • 83. What about systems maintained by other teams?
  • 84. What about systems built in outdated technologies?
  • 86. Recapping... ● Business impact of integrated tests ● Cause and explanation of those effects ● Alternative approach - isolation + contracts ● Contract-testing as an approach, Pact as a tool (in this order!)
  • 87. Thank you - @matthewfellows Given “The presentation is over” Upon Receiving “A request for an answer” With “A valid question” Respond With “A valid answer”