VCREscrevendo testes para clientes de APIs         de forma inteligente           Cássio Marques      16º encontro do GURU...
http://cassiomarques.wordpress.com         @cassiomarques        cassiommc at gmail
Sistemas secomunicam entre si
Quer seja na web
Twitter      AWSSua              FacebookApp            Seu serviço             preferido
Ou dentro da suaprópria arquitetura
Autenticação      Pedidos      Geração de                     feedsSuaApp   Middlewares para      comunicação com         ...
Mas somosresponsáveis
Queremos testar tudo       isso
Certo?
Mas é difícil!
Dependemos de um recurso externo aocódigo sendo testado
Acesso à redeTestes lentos
Testes lentosNão rodo os testes com frequência
Algumas soluções
TOCAR O FODA-SE
Acessos reais à API
(lento)
Serviço indisponível Testes quebrados
Vantagem:
Se a API mudar, o teste        quebra
stubbing
Mea culpa: a descrição  da palestra está    “imprecisa”
Testes rápidos
Não há dependência     externa
Duas formas básicas...
class ApiClient  def create(params)    # ...  endendclass SomeService  def remote_create(params)    client = ApiClient.new...
describe SomeService do  describe "#remote_create" do    before do      ApiClient.stub(:new).and_return(mock_client = mock...
Estamos testandoSomeService de forma       isolada
Isso é bom
Mas não temoscobertura alguma para      ApiClient
Outra forma
Fakewebhttps://github.com/chrisk/fakeweb
page = `curl -is http://www.google.com/`FakeWeb.register_uri :get,                     "http://www.google.com/",          ...
Webmockhttps://github.com/bblimke/webmock
stub_request(:post, "www.example.com").with(  :body    => "abc",  :headers => { Content-Length => 3 })uri = URI.parse("htt...
stub_request(:any,"www.example.com").to_return(  :body    => "abc",  :status => 200,  :headers => { Content-Length => 3 })...
Idéia geral:
Especifico uma resposta manualmente e forço   que a mesma seja       retornada
Já é um começo!
Automatizar!
VCRhttps://github.com/myronmarston/vcr
Grava as interaçõesreais entre nossa app e os serviços externos
Reproduz as interações    nas chamadas     posteriores
Rápido pra dedéu
Determinístico
VCR.config do |c|  c.cassette_library_dir = fixtures/vcr_cassettes  c.stub_with :webmock # or :fakewebend
describe ApiClient do  describe "#create" do    VCR.use_cassette("create") do      client = ApiClient.new      response = ...
fixtures/vcr_cassettes/create.yml
•request          mostra arquivo yml de                  exemplo acessando o                  Twitter.  •method         (c...
Garantir que os testesnão realizam nenhum    request real
VCR.config do |c|  c.allow_http_connections_when_no_cassette = falseend
1) PalestraVcr::Whatever should whatever     Failure/Error: described_class.new.twitter     WebMock::NetConnectNotAllowedE...
Mas e se eu quiser algo   um pouco mais      genérico?
---- !ruby/struct:VCR::HTTPInteraction  request: !ruby/struct:VCR::Request    method: :get    uri: !ruby/regexp /https://a...
Se match só com aURL não for suficiente?
• :method - The HTTP method (i.e. GET, POST, PUT or DELETE) of the  request.• :uri - The full URI of the request.• :host -...
VCR.use_cassette "whatever", :match_requests_on => [:host, :path] do  # ...end
Múltiplos ciclosrequest/response em  um mesmo teste
---- !ruby/struct:VCR::HTTPInteraction  request: !ruby/struct:VCR::Request    method: :get    uri: “http://example.com/bla...
Cassetes dinâmicos
---- !ruby/struct:VCR::HTTPInteraction  request: !ruby/struct:VCR::Request    method: :get    uri: http://example.com:80/f...
VCR.use_cassette(dynamic, :erb => { :some_arg => 123 }) do  # ...end
Atualização automática    das gravações
A_WEEK = 7 * 24 * 60 * 60VCR.use_cassette(my_cassette, :re_record_interval => A_WEEK) do  # ...end
Obrigado!
Palestra VCR
Upcoming SlideShare
Loading in …5
×

Palestra VCR

6,304 views

Published on

Slides da

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
6,304
On SlideShare
0
From Embeds
0
Number of Embeds
4,762
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Palestra VCR

    1. 1. VCREscrevendo testes para clientes de APIs de forma inteligente Cássio Marques 16º encontro do GURU-SP 11/06/2011
    2. 2. http://cassiomarques.wordpress.com @cassiomarques cassiommc at gmail
    3. 3. Sistemas secomunicam entre si
    4. 4. Quer seja na web
    5. 5. Twitter AWSSua FacebookApp Seu serviço preferido
    6. 6. Ou dentro da suaprópria arquitetura
    7. 7. Autenticação Pedidos Geração de feedsSuaApp Middlewares para comunicação com HW Whatever
    8. 8. Mas somosresponsáveis
    9. 9. Queremos testar tudo isso
    10. 10. Certo?
    11. 11. Mas é difícil!
    12. 12. Dependemos de um recurso externo aocódigo sendo testado
    13. 13. Acesso à redeTestes lentos
    14. 14. Testes lentosNão rodo os testes com frequência
    15. 15. Algumas soluções
    16. 16. TOCAR O FODA-SE
    17. 17. Acessos reais à API
    18. 18. (lento)
    19. 19. Serviço indisponível Testes quebrados
    20. 20. Vantagem:
    21. 21. Se a API mudar, o teste quebra
    22. 22. stubbing
    23. 23. Mea culpa: a descrição da palestra está “imprecisa”
    24. 24. Testes rápidos
    25. 25. Não há dependência externa
    26. 26. Duas formas básicas...
    27. 27. class ApiClient def create(params) # ... endendclass SomeService def remote_create(params) client = ApiClient.new client.create params endend
    28. 28. describe SomeService do describe "#remote_create" do before do ApiClient.stub(:new).and_return(mock_client = mock(:api_client)) @expected = ... alguma forma complicada de preparar esse objeto ... mock_client.stub(:create).and_return(@expected) end it "returns the expected object" do service.remote_create(:foo => "bar").should == expected end endend
    29. 29. Estamos testandoSomeService de forma isolada
    30. 30. Isso é bom
    31. 31. Mas não temoscobertura alguma para ApiClient
    32. 32. Outra forma
    33. 33. Fakewebhttps://github.com/chrisk/fakeweb
    34. 34. page = `curl -is http://www.google.com/`FakeWeb.register_uri :get, "http://www.google.com/", :response => pageNet::HTTP.get(URI.parse("http://www.google.com/"))# => Full response, including headers
    35. 35. Webmockhttps://github.com/bblimke/webmock
    36. 36. stub_request(:post, "www.example.com").with( :body => "abc", :headers => { Content-Length => 3 })uri = URI.parse("http://www.example.com/")req = Net::HTTP::Post.new(uri.path)req[Content-Length] = 3res = Net::HTTP.start(uri.host, uri.port) {|http| http.request(req, "abc")} # ===> Success
    37. 37. stub_request(:any,"www.example.com").to_return( :body => "abc", :status => 200, :headers => { Content-Length => 3 })Net::HTTP.get("www.example.com", /)# ===> "abc"
    38. 38. Idéia geral:
    39. 39. Especifico uma resposta manualmente e forço que a mesma seja retornada
    40. 40. Já é um começo!
    41. 41. Automatizar!
    42. 42. VCRhttps://github.com/myronmarston/vcr
    43. 43. Grava as interaçõesreais entre nossa app e os serviços externos
    44. 44. Reproduz as interações nas chamadas posteriores
    45. 45. Rápido pra dedéu
    46. 46. Determinístico
    47. 47. VCR.config do |c| c.cassette_library_dir = fixtures/vcr_cassettes c.stub_with :webmock # or :fakewebend
    48. 48. describe ApiClient do describe "#create" do VCR.use_cassette("create") do client = ApiClient.new response = JSON.parse(client.create(:foo => "bar")) response["some_key"].should == "whatever" end endend
    49. 49. fixtures/vcr_cassettes/create.yml
    50. 50. •request mostra arquivo yml de exemplo acessando o Twitter. •method (colorscheme gobo) •uri •body •headers•response •status •code •message •headers •body •http version
    51. 51. Garantir que os testesnão realizam nenhum request real
    52. 52. VCR.config do |c| c.allow_http_connections_when_no_cassette = falseend
    53. 53. 1) PalestraVcr::Whatever should whatever Failure/Error: described_class.new.twitter WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled. Unregistered request: GET https://api.twitter.com/1/users/show.json?screen_name=foobar with headers {Accept=>application/json, Accept-Encoding=>gzip;q=1.0,deflate;q=0.6,identity;q=0.3, Authorization=>OAuthoauth_consumer_key="uxUyVGxMkKvBIX6UUjD0g", oauth_nonce="eccace43473c668ac18b5d73d058f987",oauth_signature="dIYPYQGLtxlbEJmDmQ5EHPz3rmw%3D", oauth_signature_method="HMAC-SHA1",oauth_timestamp="1307752227", oauth_token="63302783-bFB4P8fPv1HrPajZMePudJnjXMh3ywaMcRua1Dtjp", oauth_version="1.0", User-Agent=>TwitterRuby Gem 1.5.0}. You can use VCR to automatically record this request and replay it later.For more details, visit the VCR documentation at: http://relishapp.com/myronmarston/vcr/v/1-10-0
    54. 54. Mas e se eu quiser algo um pouco mais genérico?
    55. 55. ---- !ruby/struct:VCR::HTTPInteraction request: !ruby/struct:VCR::Request method: :get uri: !ruby/regexp /https://api.twitter.com:443/1/users/show.json?screen_name=w+/ body: headers: accept: - application/json
    56. 56. Se match só com aURL não for suficiente?
    57. 57. • :method - The HTTP method (i.e. GET, POST, PUT or DELETE) of the request.• :uri - The full URI of the request.• :host - The host of the URI. You can use this (alone, or in combination with:path) as an alternative to :uri to cause VCR to match using a regex that matches the host.• :path - The path of the URI. You can use this (alone, or in combination with:host) as an alternative to :uri to cause VCR to match using a regex that matches the path.• :body - The body of the request. (Unsupported when you use FakeWeb.)• :headers - The request headers. (Unsupported when you use FakeWeb.)
    58. 58. VCR.use_cassette "whatever", :match_requests_on => [:host, :path] do # ...end
    59. 59. Múltiplos ciclosrequest/response em um mesmo teste
    60. 60. ---- !ruby/struct:VCR::HTTPInteraction request: !ruby/struct:VCR::Request method: :get uri: “http://example.com/blabla” # ... response: !ruby/struct:VCR::Response status: !ruby/struct:VCR::ResponseStatus code: 200 message: OK headers: # ...- !ruby/struct:VCR::HTTPInteraction request: !ruby/struct:VCR::Request method: :get uri: “http://example.com/foobar” # ... response: !ruby/struct:VCR::Response status: !ruby/struct:VCR::ResponseStatus code: 200 message: OK headers: # ...
    61. 61. Cassetes dinâmicos
    62. 62. ---- !ruby/struct:VCR::HTTPInteraction request: !ruby/struct:VCR::Request method: :get uri: http://example.com:80/foo?a=<%= some_arg %> body: headers: response: !ruby/struct:VCR::Response status: !ruby/struct:VCR::ResponseStatus code: 200 message: OK headers: # ...
    63. 63. VCR.use_cassette(dynamic, :erb => { :some_arg => 123 }) do # ...end
    64. 64. Atualização automática das gravações
    65. 65. A_WEEK = 7 * 24 * 60 * 60VCR.use_cassette(my_cassette, :re_record_interval => A_WEEK) do # ...end
    66. 66. Obrigado!

    ×