Kill external requests while testing rails applications using VCR



    Most recently, I had a problem with the fact that the tests of my application have been running for quite some time. This is due to the fact that some parts of the code like to access third-party services like iTunes and Facebook.

    Accessing third-party services during testing is evil for the following reasons:

    1. If during the execution of the tests communication problems begin, they can either pass slowly or fall completely.
    2. As already mentioned, the speed of passing tests is quite slowed down.
    3. There may be problems with limiting the number of requests by the services themselves.


    I was inspired to write this article by the post How to Stub External Services in Tests , which describes several techniques for getting rid of requests to external services and replacing them with local calls. The following methods are presented in the article:

    1. Create a stub on the sinatra, which rises while testing is in progress.
    2. Use the VCR library, which allows you to intercept all external requests with answers and write them to a file.

    From my own experience - both approaches are good for slightly different situations. Sinatra is good when there is one small request and the response JSON is known. VCR is good when someone’s SDK is already used (in my case, Koala for communicating with Facebook) which makes a request chain to someone using the internal logic of the library.

    In this article, we will focus on the use of VCR. For testing, rspec was used.

    For clarity, it is worth noting that files with a query chain (and their contents) are called cassettes in the VCR ideology. This can be seen in the name of the methods and if you look at the documentation. Well, VCR itself can be literally translated as “vidic”.

    First of all, we install VCR and webmock itselfto emulate requests outside. Also, webmock prohibits all external requests during tests. Gemfile:

    group :testdo
      ...
      gem 'webmock'
      gem 'vcr'
      ...
    end


    Then add to spec_helper.rb:

    require'webmock/rspec'require'vcr'
    VCR.configure do|c|
      c.cassette_library_dir = 'fixtures/vcr_cassettes'#указываем директорию где у нас будут лежать файлы с цепочками запросов
      c.ignore_hosts '127.0.0.1', 'localhost'
      c.hook_into :webmockend


    As an example, I will give a test of my API, which subsequently makes several requests to Facebook.

    This file contains a helper that registers a new test user and gives his last post from the
    spec / support / fb_helper.rb feed:

    moduleFbHelperdefinit_test_user
        VCR.use_cassette('fb_test_user') do
          result ={}
          test_users = Koala::Facebook::TestUsers.new(:app_id => Rails.application.config.fb_app_id, :secret => Rails.application.config.fb_app_secret)
          sandra = test_users.list.select! { |x| x["id"]=="1462678604000503" }
          @sandra_token = sandra.first['access_token']
          @graph = Koala::Facebook::API.new(@sandra_token)
          @sender_id = @graph.get_object("me")["id"]
          posts = @graph.get_connections("me", "posts")
          @post_id = posts.select {|x| x['id']=="1462678604000503_1462707207330976"}.first["id"]
          result[:sandra_token] = @sandra_token
          result[:post_id]= @post_id
          result[:sender_id] = @sender_id
          return result
        endendend


    The test spec file itself / spec / requests / facebook_register_post_request_spec.rb:

    require'spec_helper'
    describe 'проверака шэринга постов в FB'do
      let(:device) {
        FactoryGirl.create(:device,:guid=>generate_guid)
      }
      it 'должен проходить с валидными токенами и постом'do
        result = init_test_user
        start_points = device.points
        VCR.use_cassette('fb_register_post') do#используем другой файл для чтения/записи запросов
          get register_fb_post_api_path,{:id=>device.guid,:post=>result[:post_id],:sender=>result[:sender_id],:token=>result[:sandra_token]}
        end
        expect(response).to be_success
      endend


    According to the experience of his small project, the speed of passing tests decreased from 25 seconds (during an unstable connection it could calmly go in 1 minute) to 8 seconds.

    Full documentation for the library can be found here .

    Also popular now: