Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Collaborating with Contracts

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Tania Tania
June 07, 2015

Collaborating with Contracts

In the real world, when two parties collaborate, there often needs to be a shared understanding - a 'contract'. So is the case when two components of a system collaborate.

In this talk, I will share about contract tests, when they become useful, and some tools to help.

Avatar for Tania

Tania

June 07, 2015
Tweet

Other Decks in Technology

Transcript

  1. 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
  2. PERSON SERVICE Provider Consumers A B { id: 1, name:

    “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 } { id: 1, name: “Jane”, age: 26 }
  3. 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 }
  4. 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 }
  5. 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 }
  6. 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 }
  7. WHY IS THIS USEFUL? • services are only useful when

    they are used • consumer-driven contracts makes consumer expectations explicit
  8. 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 } $$$$
  9. “Let me go through the 20-page contract to see if

    they mention ‘friends’...”
  10. PLAYS WELL WITH TDD • write consumer-driven contract test for

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

    consumer C • red-green-refactor: consumer side • red-green-refactor: provider side
  12. 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
  13. 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 }
  14. Provider Consumer Service Boundary unit test on provider: use fake

    consumer tests that given the right request, it returns the right response Fake consumer
  15. 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 }
  16. 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 }
  17. 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
  18. 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
  19. 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
  20. 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!
  21. 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
  22. 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
  23. Provider Consumer Service Boundary Provider stub { id: 1, name:

    “Jane”, age: 26 } Fake consumer { id: 1, name: “Jane”, age: 26 }
  24. 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 }
  25. with(method: :get, path: "/person/1", query: ''). person_service. given("a person with

    too many friends"). upon_receiving("request for person details").
  26. 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: ... })
  27. 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)
  28. 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 }
  29. 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
  30. 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
  31. person_service. given("a person with too many friends"). upon_receiving("request for person

    details"). provider states given("a person with too many friends").
  32. 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
  33. 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”)
  34. USE CASES • making consumer expectations explicit • making sure

    both consumer and provider do their job • enabling parallel development