Убиваем внешние запросы во время тестирования rails приложений с помощью VCR
Совсем недавно, у меня возникла проблема заключающаяся в том, что тесты моего приложения довольно долго ходят. Это происходит в виду того, что некоторые части кода любят обращаться к сторонним сервисам вроде iTunes и Facebook.
Обращение к сторонним сервисам во время тестирования это зло по следующим причинам:
Eсли во время исполнения тестов начинаются проблемы со связью, то они могут либо медленно проходить, либо вовсе падать. Как уже и писалось — довольно сильно замедляется скорость прохождения тестов. Возможны проблемы с ограничением числа запросов самими сервисами. На написание данной статьи меня вдохновил пост How to Stub External Services in Tests, в котором описывается несколько методик избавление от запросов к внешним сервисам и подмена их локальными вызовами. В статье представлены следующие способы: Создать заглушку на синатре, которая поднимается на время хождения тестов. Использовать библиотеку VCR, которая позволяет перехватывать все внешние запросы с ответами и писать их в файл. По собственному опыту — оба подхода хороши для несколько разных ситуаций. Синатра хороша когда есть один небольшой запрос и известен JSON ответа. VCR хороша когда уже используется чей-то SDK (в моем случае Koala для общения с Facebook) который делает цепочку запросов к чьему-то апи используя внутреннюю логику библиотеки.В этой статье, мы остановимся на использовании VCR. Для тестирования использовался rspec.
Для ясности стоит отметить, что файлы с цепочкой запросов (и их содержимым) в идеологии VCR называются кассетами. Это можно заметить в названии методов и если посмотреть документацию. Ну, а сам VCR можно дословно перевести как «видик».
Первым делом устанавливаем сам VCR и webmock для эмуляции запросов во вне. Так же, webmock запрещает все внешние запросы во время тестов. Gemfile:
group: test do … gem 'webmock' gem 'vcr' … end Затем добавляем в 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: webmock end
В качестве примера я приведу тест своего API, который в последствии делает несколько запросов к Facebook.
В этом файле лежит хелпер который регистрирует нового тестового пользователя и отдает его последний пост из лентыspec/support/fb_helper.rb:
module FbHelper def init_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 end end end Сам файл с тестами 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 end end По опыту своего небольшого проекта, скорость прохождения тестов уменьшилась с 25 секунд (во время нестабильного коннекта могло спокойно уходить за 1 минуту) до 8 секунд.
Полную документацию на библиотеку можно посмотреть здесь.