Your SlideShare is downloading. ×
Effectively Testing Services on Rails - Railsconf 2014
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Effectively Testing Services on Rails - Railsconf 2014

503
views

Published on

Testing services with Ruby on Rails for Railsconf 2014. Test SOA or external services with ease.

Testing services with Ruby on Rails for Railsconf 2014. Test SOA or external services with ease.

Published in: Software, Technology

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
503
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
4
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • Includes: things like Stripe, or an internal API, or an iPhone app calling into an API exposed by your rails app
  • LA kings checkingchicagoblackhawks
  • Can back with yaml
  • Can back with yaml
  • Can back with yaml
  • Can back with yaml
  • Re-writing web proxyAllowed to record and reuse (like VCR)
  • Can back with yaml
  • Ubiquitous PowerfulIn-browser (so easy!)
  • Easily send requestsEasy-to-use GUI
  • Postman without the GUICould run small scripts around it
  • re-writing web proxyTest mobile as well as desktopGood for collecting a lot of responsesGood for testing things that aren’t specific page loads in ChromeGood when you don’t know what’s even being requested!
  • Transcript

    • 1. Effectively Testing Services Neal Kemp
    • 2. $ whoami Iowa native Now: Californian Software Developer Independent Consultant
    • 3. What I Do Ruby / Rails Javascript / Angular HTML, CSS, etc
    • 4. what,why&how of testing services
    • 5. NOT Building testable services
    • 6. NOT Test-driven development (necessarily)  
    • 7. … and because I don’t want @dhh to rage
    • 8. what
    • 9. What is a service? Internal “SOA”
    • 10. Any time you make an HTTP request to an endpoint in another repository
    • 11. why
    • 12. Why are services important? Build faster Makes scaling easier Use them on virtually every application Increasingly prevalent
    • 13. Services are critical to modern Rails development
    • 14. Why is testing services important? You (should) test everything else Services compose crucial features You may encounter problems…
    • 15. Internal API Sometimes null responses Inconsistencies Catastrophe
    • 16. Okay? But what about external APIs?
    • 17. {"id": 24} {"code": "ANA"}
    • 18. "goals":[ { "per":"1", "ta":"CGY", "et":"14:11", "st":"Wrist Shot" }, { "per":"2", "ta":"ANA", "et":"11:12", "st":"Backhand" } ] "goals": { "per":"1", "ta":"CGY", "et":"14:11", "st":"Wrist Shot" }
    • 19. No versioning!
    • 20. Snapchat Client Haphazard documentation What are the requests? Bizarre obfuscation github.com/nneal/snapcat
    • 21. how
    • 22. What is different about services? External network requests You don’t own the code
    • 23. On an airplane… Failure is bad! No network requests
    • 24. Don’t interact with services from test environment* **
    • 25. * Includes “dummy” APIs
    • 26. ** Using pre-recorded responses is okay
    • 27. Assuming: Rails, rspec
    • 28. Timetostub!
    • 29. Built-in Stubbing Typhoeus Faraday Excon
    • 30. Simplify.
    • 31. gem 'webmock'
    • 32. ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) require 'rspec/autorun' require 'rspec/rails’ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) RSpec.configure do |config| config.infer_base_class_for_anonymous_controllers = false config.order = 'random’ end WebMock.disable_net_connect! spec/spec_helper.rb
    • 33. module FacebookWrapper def self.user_id(username) user_data(username)['id'] end def self.user_data(username) JSON.parse( open("https://graph.facebook.com/#{username}").read ) end end lib/facebook_wrapper.rb
    • 34. require 'facebook_wrapper' config/intializers/facebook_wrapper.rb
    • 35. require 'spec_helper' describe FacebookWrapper, '.user_link' do it 'retrieves user link' do stub_request(:get, 'https://graph.facebook.com/arjun'). to_return( status: 200, headers: {}, body: '{ "id": "7901103","first_name": "Arjun", "locale": "en_US","username": "Arjun" }' ) user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103' end end spec/lib/facebook_wrapper_spec.rb
    • 36. require 'spec_helper' describe FacebookWrapper, '.user_link' do it 'retrieves user link' do stub_request(:get, 'https://graph.facebook.com/arjun'). to_return( status: 200, headers: {}, body: '{ "id": "7901103","first_name": "Arjun", "locale": "en_US","username": "Arjun" }' ) user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103' end end spec/lib/facebook_wrapper_spec.rb
    • 37. require 'spec_helper' describe FacebookWrapper, '.user_link' do it 'retrieves user link' do stub_request(:get, 'https://graph.facebook.com/arjun'). to_return( status: 200, headers: {}, body: '{ "id": "7901103","first_name": "Arjun", "locale": "en_US","username": "Arjun" }' ) user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103' end end spec/lib/facebook_wrapper_spec.rb
    • 38. require 'spec_helper' describe FacebookWrapper, '.user_link' do it 'retrieves user link' do stub_request(:get, 'https://graph.facebook.com/arjun'). to_return( status: 200, headers: {}, body: '{ "id": "7901103","first_name": "Arjun", "locale": "en_US","username": "Arjun" }' ) user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103' end end spec/lib/facebook_wrapper_spec.rb
    • 39. Even Better No network requests Fast! No intermittent failure
    • 40. Mock-Services AWS FB graph mock OmniAuth Etc…
    • 41. gem 'fb_graph-mock'
    • 42. ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) require 'rspec/autorun' require 'rspec/rails’ require 'fb_graph/mock' Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) RSpec.configure do |config| config.infer_base_class_for_anonymous_controllers = false config.order = 'random' config.include FbGraph::Mock end WebMock.disable_net_connect! spec/spec_helper.rb
    • 43. describe FacebookWrapper, '.user_link' do it 'retrieves user link' do mock_graph :get, 'arjun', 'users/arjun_public' do user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103' end end end spec/lib/facebook_wrapper_spec.rb
    • 44. describe FacebookWrapper, '.user_link' do it 'retrieves user link' do mock_graph :get, 'arjun', 'users/arjun_public' do user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103' end end end spec/lib/facebook_wrapper_spec.rb
    • 45. Even Better Already stubbed for you Pre-recorded responses (sometimes) Don’t need to know API endpoints
    • 46. gem 'sham_rack'
    • 47. gem 'sinatra'
    • 48. ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) require 'rspec/autorun' require 'rspec/rails’ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) RSpec.configure do |config| config.infer_base_class_for_anonymous_controllers = false config.order = 'random’ end WebMock.disable_net_connect! spec/spec_helper.rb
    • 49. ShamRack.at('graph.facebook.com', 443).sinatra do get '/:username' do %Q|{ "id": "7901103", "name": "Arjun Banker", "first_name": "Arjun", "last_name": "Banker", "link": "http://www.facebook.com/#{params[:username]}", "location": { "id": 114952118516947, "name": "San Francisco, California" }, "gender": "male" }| end end spec/support/fake_facebook.rb
    • 50. ShamRack.at('graph.facebook.com', 443).sinatra do get '/:username' do %Q|{ "id": "7901103", "name": "Arjun Banker", "first_name": "Arjun", "last_name": "Banker", "link": "http://www.facebook.com/#{params[:username]}", "location": { "id": 114952118516947, "name": "San Francisco, California" }, "gender": "male" }| end end spec/support/fake_facebook.rb
    • 51. ShamRack.at('graph.facebook.com', 443).sinatra do get '/:username' do %Q|{ "id": "7901103", "name": "Arjun Banker", "first_name": "Arjun", "last_name": "Banker", "link": "http://www.facebook.com/#{params[:username]}", "location": { "id": 114952118516947, "name": "San Francisco, California" }, "gender": "male" }| end end spec/support/fake_facebook.rb
    • 52. ShamRack.at('graph.facebook.com', 443).sinatra do get '/:username' do %Q|{ "id": "7901103", "name": "Arjun Banker", "first_name": "Arjun", "last_name": "Banker", "link": "http://www.facebook.com/#{params[:username]}", "location": { "id": 114952118516947, "name": "San Francisco, California" }, "gender": "male" }| end end spec/support/fake_facebook.rb
    • 53. describe FacebookWrapper, '.user_link' do it 'retrieves user link' do user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103’ end end spec/lib/facebook_wrapper_spec.rb
    • 54. Even Better Dynamic Expressive Readable
    • 55. gem 'vcr'
    • 56. ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) require 'rspec/autorun' require 'rspec/rails’ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) RSpec.configure do |config| config.infer_base_class_for_anonymous_controllers = false config.order = 'random’ end WebMock.disable_net_connect! VCR.configure do |c| c.cassette_library_dir = 'spec/fixtures/vcr_cassettes' c.hook_into :webmock end spec/spec_helper.rb
    • 57. describe FacebookWrapper, '.user_link' do it 'retrieves user link' do VCR.use_cassette('fb_user_arjun') do user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103' end end end spec/lib/facebook_wrapper_spec.rb
    • 58. describe FacebookWrapper, '.user_link' do it 'retrieves user link' do VCR.use_cassette('fb_user_arjun') do user_id = FacebookWrapper.user_id('arjun') expect(user_id).to eq '7901103' end end end spec/lib/facebook_wrapper_spec.rb
    • 59. Even Better Record API automatically Replay responses without network Verify responses
    • 60. Additional Build Process Runs outside normal test mode Rechecks cassettes for diffs Avoids versioning issues
    • 61. gem 'puffing-billy'
    • 62. Puffing-Billy Built for in-browser requests Allowed to record and reuse (like VCR)
    • 63. Be brave, venture out of ruby
    • 64. I also like…
    • 65. Chrome Dev Tools
    • 66. Postman
    • 67. HTTPie
    • 68. Charles
    • 69. Additional Reading martinfowler.com/bliki/IntegrationContractTest.html robots.thoughtbot.com/how-to-stub-external-services-in-tests joblivious.wordpress.com/2009/02/20/handling-intermittence-how-to- survive-test-driven-development railscasts.com/episodes/291-testing-with-vcr
    • 70. Bringing it all together Testing services is crucial If in doubt, stub it out Determine the flexibility you want Record responses to save time
    • 71. Next Up Eliminating Inconsistent Test Failures with Austin Putman
    • 72. Thank you! me@nealke.mp (I like emails) @neal_kemp (I tweet)

    ×