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

Collaborating with Contracts

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.

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