В любом крупном проекте, так или иначе, но приходится сталкиваться с использованием внешних сервисов. Это может быть шлюз для отправки SMS, или сервис для получения курсов валют. В этой статье я опишу как тестировать их легко и приятно.
API социальных сетей, платежных систем, и прочее прочее. Сейчас редко крупный проект обходится без привязки к внешним сервисам. Тестировать код, связанный со внешними сервисами не всегда легко, это издержки сети, медленное соединение. Лучше изолировать эти тесты с помощью заглушек.
Имитация сервиса на примере конвертера валют
Допустим у нас есть некая система, которая использует внешний сервис Fixer.io для получения курсов валют, и преобразования некой суммы в ту валюту, которую укажет клиент.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Наш код прекрасно работает на production сервере. Но мы не хотим, чтобы при тестировании приложения, отправлялись реальные запросы к сервису. Давайте посмотрим какие инструменты наиболее популярны для решения этой задачи.
Webmock
Webmock это библиотека для создания и использования заглушек HTTP запросов. Это довольно простой и удобный инструмент, и подходит для использования в связке с Rspec, Minitest, Test::Unit
Настройка Webmock проста. На странице проекта есть инструкция по установке. Она сводится к установке gem, и прописыванию библиотеки в
spec_helper.rb
или в test_helper.rb
1
|
|
1
|
|
Давайте посмотрим как будет выглядеть тест на Rspec, написанный с использованием Webmock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Все довольно просто. Теперь когда наш код в тесте будет обращаться по url http://api.fixer.io/latest?symbols=USD&base=EUR
, вызов сервиса будет заглушаться, и вместо реального запроса мы получим то, что указали в to_return
.
Webmock позволяет легко создавать заглушки для сервиса, но есть и подводные камни. Если вдруг ответ реального сервера поменялся, например поменялось API, то мы не сможем отследить это изменение через тест.
VCR
Решить проблему, обозначенную выше помогает - VCR. Его отличие от Webmock в том, что VCR записывает реальный HTTP-ответ от сервиса и использует его потом изолированно в тестах. Запись производится в YAML файл.
Настройка и установка также простая. Установить gem, и добавить конфигурационные строчки в test_helper.rb
или spec_helper.rb
1 2 3 4 5 6 7 8 |
|
Вот пример мини-теста с использованием VCR
1 2 3 4 5 6 7 |
|
Во время запуска этого теста, будет послан реальный запрос к сервису, а ответ записан в yml-файл c названием, которое было указано
как аргумент в VCR.use_cassette
.
Вот так примерно будет выглядеть ответ, записанный в файл:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
При последующем обращении к сервису, будет использоваться этот запрос, который записан в файл. Это довольно удобно, особенно когда нужно использовать один и тот же запрос в разных местах.
Внедрение зависимости (Dependency Injection)
Еще один из способов, состоит во внедрении паттерна проектирования Dependency Injection. Посмотрим на примере нашего конвертера, как можно использовать его.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Итак, мы внедрили в конструктор класса Converter, FixerAPI класс, который представляет собой обертку для работы с сервисом Fixer. Вот так выглядит наш FixerAPI класс
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Мы вынесли работу с внешним сервисом в отдельный класс, и можем его включать в любом месте где захотим. И в этом состоит суть внедрения зависимости. Так как код стал изолирован, то и тестировать его можно, заменив например FixerAPI каким-нибудь FakeAPI.
1 2 3 4 5 6 7 8 9 10 11 |
|
Написать свой тестовый сервис
Я не буду долго рассматривать этот способ, так как считаю его очень сложным. Но он имеет место быть. Если коротко, то мы можем написать тестовый сервер, который будет работать например на Sinatra. Он будет возвращать нам нужные данные. Их мы и будем использовать в наших тестах.
1 2 3 4 5 6 |
|
Заключение
Проблема вполне решаема, разными способами, но лично я остановился бы на VCR. Но конечно нужно смотреть еще на целесообразность использования того или инструмента для облегчения тестирования внешних сервисов.