Slide 1

Slide 1 text

COLLABORATING WITH CONTRACTS @stania_ang

Slide 2

Slide 2 text

REAL LIFE: COLLABORATION

Slide 3

Slide 3 text

REAL LIFE: COLLABORATION • just do it

Slide 4

Slide 4 text

REAL LIFE: COLLABORATION • just do it • negotiate a contract, adhere to it

Slide 5

Slide 5 text

REAL LIFE: CONTRACTS Provider Ice cream supplier Consumer Supermarket Contract must order 1 week in advance must pay at time of order must deliver within 3 business days product must not expire within 2 months

Slide 6

Slide 6 text

MOVING ALONG...

Slide 7

Slide 7 text

COLLABORATION: SERVICES

Slide 8

Slide 8 text

SERVICE

Slide 9

Slide 9 text

SERVICE • a reusable software component encapsulating a business function

Slide 10

Slide 10 text

SERVICE • a reusable software component encapsulating a business function • can be exposed over HTTP, queue, ...

Slide 11

Slide 11 text

Provider Consumer

Slide 12

Slide 12 text

SERVICE CONTRACTS

Slide 13

Slide 13 text

SERVICE CONTRACTS • request - response

Slide 14

Slide 14 text

SERVICE CONTRACTS • request - response • performance characteristics

Slide 15

Slide 15 text

PERSON SERVICE Provider Consumers A B { id: 1, name: “Jane”, age: 26 }

Slide 16

Slide 16 text

USE CASES

Slide 17

Slide 17 text

CASE 1

Slide 18

Slide 18 text

PERSON SERVICE Provider Consumers A B { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 }

Slide 19

Slide 19 text

NEW CONSUMER, NEW TROUBLE Provider Consumers A B { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 } C { id: 1, first_name: “Jane”, last_name: “Good”, age: 26 }

Slide 20

Slide 20 text

UPDATE PROVIDER DIRECTLY Provider Consumers A B { id: 1, first_name: “Jane”, last_name: “Good”, age: 26 } { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 } C { id: 1, first_name: “Jane”, last_name: “Good”, age: 26 }

Slide 21

Slide 21 text

CONSUMERS A AND B BREAKS Provider Consumers A B { id: 1, first_name: “Jane”, last_name: “Good”, age: 26 } { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 } C { id: 1, first_name: “Jane”, last_name: “Good”, age: 26 }

Slide 22

Slide 22 text

CONSUMER-DRIVEN CONTRACTS Provider Consumers A B C Contract A Contract B Contract C { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 } { id: 1, first_name: “Jane”, last_name: “Good”, age: 26 }

Slide 23

Slide 23 text

WHY IS THIS USEFUL?

Slide 24

Slide 24 text

WHY IS THIS USEFUL? • services are only useful when they are used

Slide 25

Slide 25 text

WHY IS THIS USEFUL? • services are only useful when they are used • consumer-driven contracts makes consumer expectations explicit

Slide 26

Slide 26 text

CONSUMER-DRIVEN CONTRACT TESTS

Slide 27

Slide 27 text

CONSUMER-DRIVEN CONTRACT TESTS all the goodness of consumer-driven contracts and more

Slide 28

Slide 28 text

rake consumer:verify rake provider:verify executable

Slide 29

Slide 29 text

can be run as part of build pipeline unit contract functional deploy

Slide 30

Slide 30 text

living documentation

Slide 31

Slide 31 text

AN EXAMPLE Provider Consumers A B { id: 1, first_name: “Jane”, last_name: “Good”, age: 26, friends: ... } { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 } C { id: 1, first_name: “Jane”, last_name: “Good”, age: 26 } $$$$

Slide 32

Slide 32 text

“Let me go through the 20-page contract to see if they mention ‘friends’...”

Slide 33

Slide 33 text

PLAYS WELL WITH TDD

Slide 34

Slide 34 text

PLAYS WELL WITH TDD • write consumer-driven contract test for consumer C

Slide 35

Slide 35 text

PLAYS WELL WITH TDD • write consumer-driven contract test for consumer C • red-green-refactor: consumer side

Slide 36

Slide 36 text

PLAYS WELL WITH TDD • write consumer-driven contract test for consumer C • red-green-refactor: consumer side • red-green-refactor: provider side

Slide 37

Slide 37 text

PLAYS WELL WITH TDD • write consumer-driven contract test for consumer C • red-green-refactor: consumer side • red-green-refactor: provider side • make sure consumer-driven contract tests for consumers A and B still passes too

Slide 38

Slide 38 text

MAKING CONSUMER EXPECTATIONS EXPLICIT

Slide 39

Slide 39 text

CASE 2

Slide 40

Slide 40 text

Provider Consumer Service Boundary Provider stub testing around service boundary { id: 1, name: “Jane”, age: 26 }

Slide 41

Slide 41 text

Provider Consumer Service Boundary Provider stub unit test on consumer: uses a provider stub verifies the consumer processes the stubbed response properly { id: 1, name: “Jane”, age: 26 }

Slide 42

Slide 42 text

Provider Consumer Service Boundary unit test on provider: use fake consumer tests that given the right request, it returns the right response Fake consumer

Slide 43

Slide 43 text

Provider Consumer Service Boundary Provider stub there could be a mismatch { id: 1, name: “Jane”, age: 26, address: ... } Fake consumer { id: 1, name: “Jane”, age: 26 }

Slide 44

Slide 44 text

Provider Consumer Service Boundary Provider stub unit tests pass, but app will blow up { id: 1, name: “Jane”, age: 26, address: ... } Fake consumer { id: 1, name: “Jane”, age: 26 }

Slide 45

Slide 45 text

HOW CAN CONTRACT TESTS HELP?

Slide 46

Slide 46 text

Provider Consumer Service Boundary Provider stub both sides are verified against the same contract { id: 1, name: “Jane”, age: 26, address: ... } Fake consumer { id: 1, name: “Jane”, age: 26 } Contract

Slide 47

Slide 47 text

Provider Consumer Service Boundary Provider stub write expectations from the consumer side { id: 1, name: “Jane”, age: 26, address: ... } Fake consumer { id: 1, name: “Jane”, age: 26 } Contract

Slide 48

Slide 48 text

Provider Consumer Service Boundary Provider stub generate the contract { id: 1, name: “Jane”, age: 26, address: ... } Fake consumer { id: 1, name: “Jane”, age: 26 } Contract

Slide 49

Slide 49 text

Provider Consumer Service Boundary Provider stub verify that the provider adheres to the same contract { id: 1, name: “Jane”, age: 26, address: ... } Fake consumer { id: 1, name: “Jane”, age: 26 } Contract fail: address is missing!

Slide 50

Slide 50 text

MAKING SURE PROVIDER AND CONSUMER DO THEIR JOB

Slide 51

Slide 51 text

CASE 3

Slide 52

Slide 52 text

another team our team a tale of 2 apps

Slide 53

Slide 53 text

another team our team in the beginning was nothing

Slide 54

Slide 54 text

another team our team Service Boundary Contract

Slide 55

Slide 55 text

another team our team Service Boundary Contract executable

Slide 56

Slide 56 text

Provider Consumer Service Boundary Provider stub consumer sends the right request { id: 1, name: “Jane”, age: 26 } Fake consumer { id: 1, name: “Jane”, age: 26 } Contract

Slide 57

Slide 57 text

Provider Consumer Service Boundary Provider stub provider returns the right response { id: 1, name: “Jane”, age: 26 } Fake consumer { id: 1, name: “Jane”, age: 26 } Contract

Slide 58

Slide 58 text

another team our team Service Boundary Contract this enables parallel development

Slide 59

Slide 59 text

ENABLING PARALLEL DEVELOPMENT

Slide 60

Slide 60 text

TOOL: PACT github.com/realestate-com-au/pact

Slide 61

Slide 61 text

Provider Consumer Service Boundary Provider stub { id: 1, name: “Jane”, age: 26 } Fake consumer { id: 1, name: “Jane”, age: 26 }

Slide 62

Slide 62 text

Provider Consumer Service Boundary Provider stub write the tests for the consumer { id: 1, name: “Jane”, age: 26 } Fake consumer { id: 1, name: “Jane”, age: 26 }

Slide 63

Slide 63 text

person_service. given("a person with too many friends"). upon_receiving("request for person details").

Slide 64

Slide 64 text

with(method: :get, path: "/person/1", query: ''). person_service. given("a person with too many friends"). upon_receiving("request for person details").

Slide 65

Slide 65 text

with(method: :get, path: "/person/1", query: ''). person_service. given("a person with too many friends"). upon_receiving("request for person details"). will_respond_with(status: 200, headers: {'Content-Type' => 'application/json; charset=utf-8'}, body: { id: 1, name: “Jane”, age: 26, address: ... })

Slide 66

Slide 66 text

with(method: :get, path: "/person/1", query: ''). person_service. given("a person with too many friends"). upon_receiving("request for person details"). will_respond_with(status: 200, headers: {'Content-Type' => 'application/json; charset=utf-8'}, body: { id: 1, name: “Jane”, age: 26, address: ... }) expect(subject.person_details(“1”)).to eq(expected_person)

Slide 67

Slide 67 text

Provider Consumer Service Boundary Provider stub run watch it fail make it pass { id: 1, name: “Jane”, age: 26 } Fake consumer { id: 1, name: “Jane”, age: 26 }

Slide 68

Slide 68 text

Provider Consumer Service Boundary Provider stub the consumer tests generates a “pact” { id: 1, name: “Jane”, age: 26 } Fake consumer { id: 1, name: “Jane”, age: 26 } Pact

Slide 69

Slide 69 text

Provider Consumer Service Boundary Provider stub configure to verify selected pact(s) { id: 1, name: “Jane”, age: 26 } Fake consumer { id: 1, name: “Jane”, age: 26 } Pact

Slide 70

Slide 70 text

Provider rake pact:verify Pact controller serializer service model actual HTTP requests Fake consumer

Slide 71

Slide 71 text

MORE ON PACT

Slide 72

Slide 72 text

standalone stub Out-of-process provider stub { id: 1, name: “Jane”, age: 26 } Actual consumer

Slide 73

Slide 73 text

person_service. given("a person with too many friends"). upon_receiving("request for person details"). provider states given("a person with too many friends").

Slide 74

Slide 74 text

person_service. given("a person with too many friends"). upon_receiving("request for person details"). provider states provider_state "a person with too many friends" do set_up do 10_000.times do person = Person.first person.friends << Person.new(...) end end end

Slide 75

Slide 75 text

will_respond_with(status: 200, headers: {'Content-Type' => 'application/json; charset=utf-8'}, body: { id: 1, name: Pact::SomethingLike.new(“Jane”), age: 26, address: ... }) loose matching with(method: :get, path: "/person/1", query: ''). person_service. given("a person with too many friends"). upon_receiving("request for person details"). Pact::SomethingLike.new(“Jane”)

Slide 76

Slide 76 text

SUMMING UP

Slide 77

Slide 77 text

USE CASES

Slide 78

Slide 78 text

USE CASES • making consumer expectations explicit

Slide 79

Slide 79 text

USE CASES • making consumer expectations explicit • making sure both consumer and provider do their job

Slide 80

Slide 80 text

USE CASES • making consumer expectations explicit • making sure both consumer and provider do their job • enabling parallel development

Slide 81

Slide 81 text

THE DEVIL IS IN THE DETAILS

Slide 82

Slide 82 text

YMMV

Slide 83

Slide 83 text

SEE COLLABORATION? THINK CONTRACTS

Slide 84

Slide 84 text

THANK YOU