Talk from Nov 8th 2016 at MuCon London (https://skillsmatter.com/conferences/7412-con-2016-the-microservices-conference)
Autonomy and isolation are some of the core values of microservices, allowing for independent changes and independent deployments. As loosely coupled services interact on interfaces managed under different life-cycles and even different teams, making sure that a simple change did not break the application can turn into an integration nightmare.
Consumer-Driven Contracts testing brings an alternative integration testing approach for distributed systems, relying less on live-like integration environments and more on making interactions explicit and quickly verifiable.
This talk cover how Newsweaver (https://www.newsweaver.com/email-overview) has made CDCs part of its pipeline with Pact and how it improved collaboration and confidence between teams when designing APIs.
11. Consumer
Provider
Provider State
Request
Expected
Response
Login Service
User Service
Given that user 'pierre' exists
Method GET
Path /users/pierre
Headers
Accept: application/json
Status 200
Headers
Content-Type: application/json
Body
{
"user": "pierre",
"name": "Pierre Vincent",
"role": "publisher"
}
Interaction
12. User
Service
A
P
I
1. Set Provider State
2. Send Request
3. Verify Response
Interaction Verification Test
INSERT INTO users [...]
GET /users/pierre
Accept: application/json
{
"user": "pierre",
...
}
{
"user": "pierre",
...
}
?
15. Provider pipeline
Implement changes Get Pacts from Broker
Replay & Verify
Interactions
Deploy Service
Build
Deploy to EU
PROD-EU
Get Pacts from Broker
Replay & Verify
Interactions
Stop deployment of
incompatible Provider
Stop introduction of
breaking change
PROD-US
PROD-EU
18. Provider state setup for each interaction
Confidence comes from coverage:
don’t limit to happy paths
Automation within
deployment pipeline isn’t trivial
What’s the catch…?
19. After 2 years of CDCs...
Increased confidence when coding & deploying
✓
Collaborative API design
✓
Living API documentation
✓
20. Pact docs.pact.io, github.com/pact-foundation
Pact Broker github.com/bethesque/pact_broker
Try it out!
“Pact Matrix” (Beth Skurrie) rea.tech/enter-the-pact-matrix-or-how-to-decouple-the-release-
cycles-of-your-microservices
Why should you use CDC for microservices integration tests
techblog.newsweaver.com/why-should-you-use-consumer-driven-contracts-for-microservices-
integration-tests
Sharing CDCs with Pact Broker
techblog.newsweaver.com/sharing-consumer-driven-contracts-with-pact-broker
More reading...
Touchy subject of microservices: integration tests
If worked with > you know challenges when it comes to testing services together
CDC is an approach to reduce these headaches
Surfaced back yesterday in the thoughtworks tech radar as technique to adopt
Been working for >8 years in Newsweaver
SaaS company, Internal Comms platform/services for large enterprises
improve and unify communication across channels by giving them tools to manage their content/campaigns and measuring engagement across channels
Worked 6 years on large J2EE app
Still going great, making for 90% of the company’s income
2 years ago, moved to a new team
New product, opportunity to focus on Continuous Delivery
Microservices emerged as our architecture of choice
Now all of our new work is on microservices / splitting the monolith
Rarely start from scratch - even if we do, we’ve usually been working on this system (left)
- large, with components (if lucky), on one single DB etc
- typically called monolith
Then moving to microservices, look like this (right)
- boundaries are clearer, concepts more clearly separate
What’s missing is the interactions
- in monolith, implicit, compile time, hard to break
- in micro, more central, less validation (almost none!), easy to break if not careful
Breaking example of API backward compatibility:
Provider tests are all passing in build
Provider deploys new version
Some services then start throwing errors, even though they have not changed
Why?
Interactions got broken
How do we test the interactions
What are we testing?
Can we login?
Are we breaking an interaction?
Who should test that? At what stage?
Testing is there to stop us from doing something stupid
We want to know as early as possible...
The problem with end to end testing
Why was this wrong?
- team responsible for users need to concern themselves more than what they should
- environment will take time to come up
This is turning into a full e2e acceptance test.
- there might still be value for e2e AT in the pipeline
- but this is not what we’re trying to test here
- temptation to think e2e AT cover everything
Can we test that small thing here earlier?
- anything we get faster feedback on is a win for our pipeline time
In general e2e integration is really not practical
- illusion of coverage (explosion of potential branches as nb of services go up)- slow, painful to maintain (e.g. UI based)
- if it’s slow, this is stopping us from deploy (or even fixing forward)
How do we get rid of this? This is where contracts testing comes in...
We wanted to test the interaction - the idea behind CDC is to put the interaction at the center of the problem.
By creating a contract between the 2 services, it makes things more explicit
This is not an API specification that we write once and drift away from as the weeks go by!
Consumers will have to ensure that they are using the provider in the way the contract specified
Providers will have to ensure that they work the way the contract specified
These contracts need to be tested and validated
I will talk more specifically about PACT, framework we are using
Some people roll their own thing, and it’s perfectly fine - PACT has worked great for our workflow, so why reinvent the wheel
Pact terminology = a Pact (or Pact file) is a CDC between 2 services
Pact spec = what the pact file contains, will go in more details, but quite simple - specify how interactions are defined
Verif as Tolerant Reader / Postel’s Law: allows for flexibility in API evolution
- ex. If provider starts sending back more fields, consumers shouldn’t be considered as broken (should ignore the new fields)
- steers away for API versioning nightmares
Implementation guidelines for the verification.
Support in a lot of languages - on both sides of the puzzle (consumer / provider)
Both parties are responsible for the interactions
Consumer can define their expectations
Provider can check that they are fulfilling them
Contracts:
Consumer define a contract, which is a set of interactions
Interaction = request, response (in short)
Provider verifies interactions by replaying requests and checking responses
Benefits: Share the work, with each side keeping their concerns. Each step can be done separately, repeatedly and on their own time
What’s inside the PACT file?
Request: also query params, POST body...
Replaying interaction
Convert “plain english” provider state into state data (up to provider - consumer doesnt need to know db schema)
Start service (or even faster, use test framework support to start in junit for ex)
Replay the request from the contract
Finally compare response with expected one
Comparison follows tolerant reader pattern
Comparison has flexibility, e.g. matchers on type only or regexps (allows for better flexibility - e.g. for generated fields)
How do we share the PACTs?
For small number of services/teams, start by copy/pasting - we did this for more than a year and it was fine (with 2 teams)
Checking in repo and using URLs is also possible
When it starts becoming painful, Pact Broker will help to scale up
Pact Broker
Consumers push the contracts they have generated
Providers can query the broker for all contracts consumers have with them
So it’s just some database of contracts?
Kind of, but with a few added bonuses…
Provider build workflow
> Fast feedback during development
> Find out unindented breaking change before commit
Deployment workflow
> Important if gap of time between build and deploy
> Important if deploying to different environment (in that case there can be different PROD tags)
Prevented breaking before deploying anything! (didn’t even break a test server!)
HEAD pacts are “tentative” contracts that need to be verified by each related providers before the service can go forward.
Provider pact tests are still unit tests, very quick to run - still no need to deploy anything.
Original advantage: cover more interactions, faster, increase confidence in having backward compatible API.
Extra finding: PACTs become very central to our way to work
Team discuss them first thing when planning a new interaction
Pacts against services that rely on state (e.g. with saved data) will require some setup logic (catered for with the assumptions piece)
Large number of interactions and assumed starting state can put some pressure on the provider
Confidence from the Pacts comes from interaction coverage
Consumer tests must interact with mock and assert results, not check responses directly
Beware of only testing happy paths. Errors are also interactions.
But! Beware of being overzealous - this is probably not a place to test the Provider’s business logic (e.g. validation criteria)
Integration in Build/Deploy workflow is key to scale
Automating the verification prevents mistakes (CD book is all about this)
Can be trickier when dealing with multiple live environments but tags are very powerful with the Broker
Take the time to think about your flow and where the tests should sit best
Increased in confidence when coding & deploying
> Break fast, without having to deploy anything anywhere
> Saved us many times (forgot consumers, forgot use case…)
Relying less and less on full e2e env and tests
> Reserved for acceptance and synthetic
API design at the forefront
> Interactions are discussed early “let’s see what the pact would look like”
Consumer-driven benefits
> API are designed with consumer in mind
> Consumers are responsible for documenting their usage in PACT (“please don’t break this”)
API documents itself as it’s being used
> New consumers can check out existing pacts for examples of usage