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

Testing Services with Pact

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →

Testing Services with Pact

Integration testing services is hard but critical to ensure all your internal APIs conform, do not break their consumers but only provide the minimum required.

Pact's (https://github.com/realestate-com-au/pact) implementation of consumer driven contracts goes a long way to solving these issues.

Avatar for Gabriel Rotbart

Gabriel Rotbart

February 25, 2015

Other Decks in Programming

Transcript

  1. Consumer [noun]: The  thing  that  sends  a  specific   request

     to  a  specific  end  point   for  a  response  in  a  specific   format
  2. Provider [noun]: The  thing  that  gets  asked  for   stuff

     and  provides  the  agreed   stuff  in  an  agreed  format
  3. Pact Specify  a  contract  during   consumer  testing Generates  tests

     that  verify   the  contract  during  provider   testing
  4. flow Consumer Provider Specify an API in spec Make spec

    green Implement API contract by making spec green
  5. Round one: consumer speccing describe  FeedMeService  do   describe  ‘asking

     fat  man  for  food’  do   it  'he  provides  (or  dies)'  do   end   end   end
  6. Round one: consumer speccing before  do    fat_man_api   .upon_receiving("a

     request  to  feed  me”)   .with(   method:  :get,   path:  ‘/feed',   query:  {   food:'My  favourite  food’,   who:  ‘stewie'   }   ).will_respond_with(   status:  200,   headers:  {  'Content-­‐Type'  =>  ‘application/json  },   body:  {  food:  "Scooby-­‐Doo  yogurt"  }    )   end
  7. Round one: consumer speccing fat_man_api   .upon_receiving("a  request  to  feed

     me”)   .with(   method:  :get,   path:  ‘/feed',   query:  {   food:'My  favourite  food’,   who:  ‘stewie'   }   ).will_respond_with(   status:  200,   headers:  {  'Content-­‐Type'  =>  ‘application/json  },   body:  {  food:  "Scooby-­‐Doo  yogurt"  }    )
  8. Round one: consumer speccing fat_man_api   .upon_receiving("a  request  to  feed

     me”)   .with(   method:  :get,   path:  ‘/feed',   query:  {   food:'My  favourite  food’,   who:  ‘stewie'   }   ).will_respond_with(   status:  200,   headers:  {  'Content-­‐Type'  =>  ‘application/json  },   body:  {  food:  "Scooby-­‐Doo  yogurt"  }    )
  9. Round one: consumer speccing fat_man_api   .upon_receiving("a  request  to  feed

     me”)   .with(   method:  :get,   path:  ‘/feed',   query:  {   food:'My  favourite  food’,   who:  ‘stewie'   }   ).will_respond_with(   status:  200,   headers:  {  'Content-­‐Type'  =>  ‘application/json  },   body:  {  food:  "Scooby-­‐Doo  yogurt"  }    )
  10. Round one: consumer speccing .with(   method:  :post,   path:

     ‘/invent’,   body:  {   invention:  ‘Death  Ray’   }
  11. Round one: consumer speccing fat_man_api   .upon_receiving("a  request  to  feed

     me”)   .with(   method:  :get,   path:  ‘/feed',   query:  {   food:'My  favourite  food’,   who:  ‘stewie'   }   ).will_respond_with(   status:  200,   headers:  {  'Content-­‐Type'  =>  ‘application/json  },   body:  {  food:  "Scooby-­‐Doo  yogurt"  }    )
  12. Round one: consumer speccing it  'he  provides  (or  dies)'  do

      food  =  FeedMeService.ask_fat_man_for('My  favourite  food')   expect(food).to  eq({  'food'  =>  "Scooby-­‐Doo  yogurt  with   Shaggy's  eyes  scratched  out"  })             end
  13. Round one:Mock Service Consumer Mock service (Fat Man) GET  /feed?food=…

    When  (GET  /feed…)   received Respond  with   {  food:  …  } {  food:  …  }
  14. Round one point five: JSON file { "consumer": { "name":

    "Stewie" }, "provider": { "name": "Fat man" }, "interactions": [ { "description": "a request to feed me", "request": { "method": "get", "path": "/feed", "query": "food=My%20favourite%20food&who=stewie" }, "response": { "status": 200, "headers": { "Content-Type": "application/json; charset=utf-8" }, "body": { "food": "Scooby-Doo yogurt with Shaggy's" } } } ], "metadata": { "pactSpecificationVersion": "1.0.0" } }
  15. Round Two: Provider verifying Pact.service_provider  “Fat  Man"  do    

     honours_pact_with  'Stewie'  do          pact_uri  ‘~/stewie/spec/pacts/stewie-­‐fat_man.json’      end   end
  16. Round Two: Provider verifying auto generated tests it  ‘returns  200’

     do { "consumer": { "name": "Stewie" }, "provider": { "name": "Fat man" }, "interactions": [ { "description": "a request to feed me", "request": { "method": "get", "path": "/feed", "query": "food=My%20favourite%20food&who=stewie" }, "response": { "status": 200, "headers": { "Content-Type": "application/json; charset=utf-8" }, "body": { "food": "Scooby-Doo yogurt with Shaggy's" } } } ], "metadata": { "pactSpecificationVersion": "1.0.0" } } GET  /feed?food=… expect   (response.status)   .to  eq(200) end Provider
  17. Round Two: Provider verifying Verifying  a  pact  between  Stewie  and

     Fat  man      A  request  to  feed  me          with  GET  /feed?food=My  favourite  food&who=stewie              returns  a  response  which                  has  status  code  200  (FAILED  -­‐  1)                  has  a  matching  body  (FAILED  -­‐  2)                  includes  headers                      "Content-­‐Type"  with  value  "application/ json"  (FAILED  -­‐  3)
  18. Round Two: Provider verifying return  render  nothing:  true,    

    status:  :not_found  if  params[:who]  ==  'meg'   if  params[:who]  ==  'stewie'  &&  params[:food]  ==  URI.decode('My   favourite  food’)   return  render  json:  {  food:  "Scooby-­‐Doo  yogurt  with  Shaggy's   eyes  scratched  out"  }   end
  19. Round Two: Provider verifying return  render  nothing:  true,    

    status:  :not_found  if  params[:who]  ==  'meg'   if  params[:who]  ==  'stewie'  &&  params[:food]  ==  URI.decode('My   favourite  food’)   return  render  json:  {  food:  "Scooby-­‐Doo  yogurt  with  Shaggy's   eyes  scratched  out"  }   end For Demonstration Only
  20. Round Two: Provider verifying Verifying  a  pact  between  Stewie  and

     Fat  man      A  request  to  feed  me          with  GET  /feed?food=My  favourite  food&who=stewie              returns  a  response  which                  has  status  code  200                  has  a  matching  body                  includes  headers                      "Content-­‐Type"  with  value  “application/json"
  21. s3