Совсем недавно, у меня возникла проблема заключающаяся в том, что тесты моего приложения довольно долго ходят. Это происходит в виду того, что некоторые части кода любят обращаться к сторонним сервисам вроде 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 секунд.
Полную документацию на библиотеку можно посмотреть здесь.
Автор: r00t_aka_spin