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. COLLABORATING WITH
    CONTRACTS
    @stania_ang

    View Slide

  2. REAL LIFE: COLLABORATION

    View Slide

  3. REAL LIFE: COLLABORATION
    • just do it

    View Slide

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

    View Slide

  5. 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

    View Slide

  6. MOVING ALONG...

    View Slide

  7. COLLABORATION: SERVICES

    View Slide

  8. SERVICE

    View Slide

  9. SERVICE
    • a reusable software component encapsulating a business
    function

    View Slide

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

    View Slide

  11. Provider
    Consumer

    View Slide

  12. SERVICE CONTRACTS

    View Slide

  13. SERVICE CONTRACTS
    • request - response

    View Slide

  14. SERVICE CONTRACTS
    • request - response
    • performance characteristics

    View Slide

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

    View Slide

  16. USE CASES

    View Slide

  17. CASE 1

    View Slide

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

    View Slide

  19. 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
    }

    View Slide

  20. 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
    }

    View Slide

  21. 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
    }

    View Slide

  22. 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
    }

    View Slide

  23. WHY IS THIS USEFUL?

    View Slide

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

    View Slide

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

    View Slide

  26. CONSUMER-DRIVEN
    CONTRACT TESTS

    View Slide

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

    View Slide

  28. rake consumer:verify
    rake provider:verify
    executable

    View Slide

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

    View Slide

  30. living documentation

    View Slide

  31. 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
    }
    $$$$

    View Slide

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

    View Slide

  33. PLAYS WELL WITH TDD

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. 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

    View Slide

  38. MAKING CONSUMER
    EXPECTATIONS EXPLICIT

    View Slide

  39. CASE 2

    View Slide

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

    View Slide

  41. 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
    }

    View Slide

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

    View Slide

  43. 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
    }

    View Slide

  44. 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
    }

    View Slide

  45. HOW CAN CONTRACT TESTS
    HELP?

    View Slide

  46. 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

    View Slide

  47. 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

    View Slide

  48. 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

    View Slide

  49. 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!

    View Slide

  50. MAKING SURE PROVIDER AND
    CONSUMER DO THEIR JOB

    View Slide

  51. CASE 3

    View Slide

  52. another team
    our team
    a tale of 2 apps

    View Slide

  53. another team
    our team
    in the beginning was nothing

    View Slide

  54. another team
    our team
    Service
    Boundary
    Contract

    View Slide

  55. another team
    our team
    Service
    Boundary
    Contract
    executable

    View Slide

  56. 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

    View Slide

  57. 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

    View Slide

  58. another team
    our team
    Service
    Boundary
    Contract
    this enables parallel development

    View Slide

  59. ENABLING PARALLEL
    DEVELOPMENT

    View Slide

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

    View Slide

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

    View Slide

  62. 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
    }

    View Slide

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

    View Slide

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

    View Slide

  65. 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: ...
    })

    View Slide

  66. 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)

    View Slide

  67. 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
    }

    View Slide

  68. 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

    View Slide

  69. 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

    View Slide

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

    View Slide

  71. MORE ON PACT

    View Slide

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

    View Slide

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

    View Slide

  74. 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

    View Slide

  75. 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”)

    View Slide

  76. SUMMING UP

    View Slide

  77. USE CASES

    View Slide

  78. USE CASES
    • making consumer expectations explicit

    View Slide

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

    View Slide

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

    View Slide

  81. THE DEVIL IS IN THE DETAILS

    View Slide

  82. YMMV

    View Slide

  83. SEE COLLABORATION?
    THINK CONTRACTS

    View Slide

  84. THANK YOU

    View Slide