Testing Services Effectively

419 views

Published on

Testing Services Effectively with Ruby

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
419
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Testing Services Effectively

  1. 1. TESTING SERVICES effectively Alberto Leal @albertoleal
  2. 2. http://albertoleal.me
  3. 3. What is a service?
  4. 4. External
 Service Internal Service
  5. 5. ad$ A
  6. 6. http://api.example.com/cars Request Response
  7. 7. Connection: keep-alive Content-Length: 1466 Content-Type: application/json; Date: Sat, 02 Aug 2014 19:42:38 GMT ETag: W/"5ba-70348105" { "cars": [ { "created": "2014-07-25T19:27:56.919Z", "id": "53d2afbc7165355f0eaf79da", "modified": "2014-07-25T19:27:56.919Z", "name": "Ferrari" }, { "created": "2014-07-25T19:27:56.874Z", "id": "53d2afbc7165355f0eaf79d9", "modified": "2014-07-25T19:27:56.874Z", "name": "Porshe" } ] } Request Response http://api.example.com/cars
  8. 8. Connection: keep-alive Content-Length: 1466 Content-Type: application/json; Date: Sat, 02 Aug 2014 19:42:38 GMT ETag: W/"5ba-70348105" { "cars": [ { "created": "2014-07-25T19:27:56.919Z", "id": "53d2afbc7165355f0eaf79da", "modified": "2014-07-25T19:27:56.919Z", "name": "Ferrari" }, { "created": "2014-07-25T19:27:56.874Z", "id": "53d2afbc7165355f0eaf79d9", "modified": "2014-07-25T19:27:56.874Z", "name": "Porshe" } ] } headers
  9. 9. Connection: keep-alive Content-Length: 1466 Content-Type: application/json; Date: Sat, 02 Aug 2014 19:42:38 GMT ETag: W/"5ba-70348105" { "cars": [ { "created": "2014-07-25T19:27:56.919Z", "id": "53d2afbc7165355f0eaf79da", "modified": "2014-07-25T19:27:56.919Z", "name": "Ferrari" }, { "created": "2014-07-25T19:27:56.874Z", "id": "53d2afbc7165355f0eaf79d9", "modified": "2014-07-25T19:27:56.874Z", "name": "Porshe" } ] } body
  10. 10. Failures: ! 1) Car Service should retrieve all cars Failure/Error: response = RestClient.get ‘api.example.com/cars' SocketError: getaddrinfo: nodename nor servname provided, or not known # ./test_spec.rb:12:in `block (2 levels) in <top (required)>' ! Finished in 0.02436 seconds (files took 0.81924 seconds to load) 1 example, 1 failure ! Failed examples: ! rspec ./test_spec.rb:11 # External Service should retrieve all cars
  11. 11. gem 'webmock' https://github.com/bblimke/webmock
  12. 12. require 'webmock/rspec' require 'rest_client' ! describe 'Car Service’ do it 'should retrieve all cars' do response = RestClient.get ‘api.example.com/cars' expect(response).to be_an_instance_of(String) end end
  13. 13. ------------------------------ FAIL: 1 PASS: 0 PENDING: 0 ------------------------------ Finished in 0.04571 seconds [Car Service] - should retrieve all cars Real HTTP connections are disabled. Unregistered request: GET http:// api.example.com/cars with headers {'Accept'=>'*/*; q=0.5, application/ xml', 'Accept-Encoding'=>'gzip, deflate', 'User-Agent'=>'Ruby'}You can stub this request with the following snippet:stub_request(:get, “http:// api.example.com/cars”). with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'User- Agent'=>'Ruby'}). to_return(:status => 200, :body => "", :headers => {})
  14. 14. require 'webmock/rspec' require 'rest_client' ! WebMock.allow_net_connect! ! describe 'Car Service' do it 'should retrieve all cars' do response = RestClient.get ‘api.example.com/cars' expect(response).to be_an_instance_of(String) end end
  15. 15. require 'webmock/rspec' require 'rest_client' ! # WebMock.allow_net_connect! ! describe 'Car Service' do before :all do stub_request(:get, ‘api.example.com/cars') .to_return(status: 200, headers: {}, body: 'This is a mock.') end ! it 'should retrieve all cars' do response = RestClient.get ‘http://api.example.com/cars' expect(response).to eq(‘This is a mock.') end end
  16. 16. gem 'vcr'gem 'vcr' https://github.com/vcr/vcr
  17. 17. ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'rspec/autorun' ! RSpec.configure do |config| config.mock_with :rspec config.infer_base_class_for_anonymous_controllers = false config.order = "random" ! #Configuring VCR gem config.around(:each, :vcr) do |example| opts = example.metadata.slice(:record, :match_requests_on).except(:example_group) VCR.use_cassette(example.metadata[:cassette_name], opts) { example.call } end end
  18. 18. describe Car do ... describe '.all' do it 'should retrieve all available cars', :vcr, cassette_name: ‘cars/all’ do cars = Car.all expect(cars).to be_an_instance_of Array expect(cars).to have(2).items end end ! ... end
  19. 19. gem 'puffing-billy' https://github.com/oesmith/puffing-billy
  20. 20. require ‘billy/rspec' ! Capybara.javascript_driver = :selenium_billy
  21. 21. proxy.stub(‘http://api.example.com/cars').and_return(json: {cars: […]})
  22. 22. Integration
 Tests Contract Tests
  23. 23. Consumer-Driven Contracts: A Service Evolution Pattern http://martinfowler.com/articles/consumerDrivenContracts.html
  24. 24. Contracts can couple service providers and consumers
  25. 25. gem 'pact' https://github.com/realestate-com-au/pact
  26. 26. class MyServiceProviderClient include HTTParty base_uri 'http://my-service' ! def get_something name = JSON.parse(self.class.get("/something").body)['name'] Something.new(name) end end Client
  27. 27. require 'pact/consumer/rspec' ! Pact.service_consumer "My Service Consumer" do has_pact_with "My Service Provider" do mock_service :my_service_provider do port 1234 end end end Mock Server
  28. 28. describe MyServiceProviderClient, pact: true do ! before do # Configure your client to point to the stub service on localhost using the port you have specified MyServiceProviderClient.base_uri 'localhost:1234' end ! subject { MyServiceProviderClient.new } ! describe "get_something" do ! before do my_service_provider.given("something exists"). upon_receiving("a request for something").with(method: :get, path: '/something'). will_respond_with( status: 200, headers: {'Content-Type' => 'application/json'}, body: {name: 'A small something'} ) end ! it "returns a Something" do expect(subject.get_something).to eq(Something.new('A small something')) end ! end ! end
  29. 29. thanks! http://albertoleal.me
  30. 30. https://www.flickr.com/photos/robsmits/5452184206 https://www.flickr.com/photos/hotcherry/521006473 Photos: https://www.flickr.com/photos/vern/5379218273/sizes/l

×