Современные приложения редко работают в изоляции, чаще всего они интегрируются с множеством сторонних сервисов. Поэтому остро встает вопрос тестирования этой интеграции в рамках интеграционного или функционального тестирования. И тут можно наступить на многие грабли, о которых я и собираюсь поговорить. Для кого эта тема актуальна, прошу под кат.
В чем собственно проблема?
Первое решение, которое приходит в голову — тестировать напрямую на реальном сервисе. В некоторых случаях это может быть обосновано, но часто приводит к следующим проблемам:
- Тесты замедляются достаточно сильно. Доступ к внешнему сервису обычно осуществляется по сети с использованием публичных протоколов, что гораздо медленнее локальной интеграции. Внешний сервис обычно не такой быстрый и подвержен собственной нагрузке, во время которой может замедляться еще сильнее. В результате я встречал на практике замедление интеграционного набора тестов с 10 минут до пары часов, причем время выполнения сильно плавало.
- Тесты становятся менее стабильными. Как я уже говорил, внешние сервисы подвержены нагрузкам, иногда недоступны из-за обновлений, «падают» при неверном обновлении разработчиками сервиса, ну и сетевые ошибки также не исключены. Все это очень сильно влияет на ваш тестовый набор и его стабильность. Тесты «падают» неожиданно, на изучение проблемы уходит много времени и члены команды начинают недоверчиво относиться к тестам в целом. В результате сама полезность тестирования может быть поставлена под сомнение.
- Вы просто не имеете возможность получить некоторые ответы внешнего сервиса (ошибки, специфические данные и т.д.), что может сказаться на покрытии вашего кода тестами, а значит надежности регрессионного тестирования интеграционными тестами. Ведь нужно покрывать не только позитивные, но и негативные сценарии.
- Реальный сервис не всегда имеет тестовый интерфейс и вам приходится использовать совершенно реальный. А это не всегда бесплатно и любые ошибки в вашем коде подвергают риску реальные данные на внешнем сервисе. Благодаря этой проблеме можно легко удалить очень важные данные или заблокировать доступ из реального приложения. И это не говоря о том, что много денег может быть потрачено на «неожиданное» использование внешнего сервиса.
Описанные проблемы заставляют вас искать альтернативные решения и они существуют.
Заглушки правят миром тестирования!
На помощь вам могут придти разнообразные варианты заглушек (не нашел лучшего перевода термина mock objects). Есть несколько вариантов заглушек: mock, stub, dummy, spy, fake. Знать и различать их нужно и важно, поэтому стоит почитать и разобраться в специфике, но речь пойдет не об этом. Нам понадобится только один из видов заглушек, который наиболее подходит для работы с внешними сервисами — fake реализация.
Fake реализация представляет из себя упрощенный вариант настоящего сервиса, который работает полностью предсказуемо, локально, дает полный доступ к данным и настройкам, а также не требует никакой дополнительной работы в каждом тесте. Таким образом, мы сможем прозрачно заменить настоящий сервис в тестах на fake реализацию и решить все проблемы. Звучит просто? Не очень!
Во-первых, нам нужно будет реализовать этот упрощенный вариант сервиса, на что понадобится время и усилия разработчиков. Вы счастливчик, если ваш внешний сервис предоставляет fake реализацию для локального тестирования. Тогда вы просто скачиваете библиотеку, настраиваете, запускаете и пользуетесь на здоровье. Некоторые сервисы позволяют получить доступ к специальной версии внешнего сервиса для тестирования, которая не подвержена перечисленным в первой части проблемам. Но таких сервисов единицы. Почти всегда вам придется писать fake реализацию самостоятельно.
Во-вторых, вам придется позаботиться о том, чтобы ваша fake реализация ведет себя так же как реальный сервис. Это одни из самых частых граблей, на которые наступают сторонники описанного подхода. Вы делаете fake реализацию сегодня, а через неделю реальный сервис начинает вести себя по-другому. Ваши тесты по-прежнему проходят и вы спокойно живете пока не задеплоите ваш код на «живые» сервера. И вот там вас ждет неприятный сюрприз!
Эту проблему очень легко исправить, но придется приложить еще немного усилий. Вам нужно написать еще один набор тестов, теперь уже на реальный сервис, целью которого будет контроль и поддержание надежности протокола между вашим приложением и сторонним сервисом. Это некоторый вариант smoke тестов на внешний сервис, которые можно запускать раз в час, день или неделю в зависимости от стабильности вашего внешнего сервиса. Тогда вы действительно контролируете и тестируете интеграцию с внешним сервисом и избегаете сюрпризов при его изменении.
Я не готов к таким изменениям!
Описанное решение подходит не всем (в принципе как и любое другое). Поэтому я дам несколько советов, с помощью которых вы можете немного сгладить проблемы из первой части этой статьи.
- Разделяйте тесты на те, где вам нужна интеграция в внешними сервисами, и те, где важна только внутренняя функциональность вашего продукта. Приведу пару простых примеров. Если вы тестируете регистрацию, то вам наверняка не так важна система показа рекламы, подтягивание последних записей из Twitter и т.д. Поэтому внешние сервисы можно и нужно отключать. В каждом приложении это можно сделать по-своему. Например, в веб-приложении очень просто использовать контролируемый прокси, который может в любое время перекрыть доступ к одному из внешних сервисов. Такая простая мера позволит вам очень сильно ускорить ваши тесты.
- Активно используйте настраиваемое кэширование для получения данных из внешних сервисов. Обычно при интеграционном или функциональном тестировании вы проходите одни и те же части приложения неоднократно, при этом не всегда есть потребность в обновленных данных. Поэтому можно сильно сэкономить на вызовах внешних сервисов, закэшировав результаты предыдущего вызова. Таким образом, вы все еще тестируете получение реальных данных, но гораздо оптимальнее.
- Обязательно поищите open source fake решение для вашего сервиса. Обычно для популярных сервисов оно существует, так как с подобной задачей тестирования сталкивается много людей и не все из них такие ленивые как вы. Например, есть замечательный проект fake-s3 для работы с Amazon S3 сервисом. Он реализует тот же API, позволяя тестировать интеграцию бесплатно, безопасно и локально. Все что вам необходимо — это возможность конфигурации доступа к сервису в вашем приложении.
- Узнайте у провайдера сервиса, есть ли у него возможности для локализации тестирования. Возможно, вы просто не знаете о существовании подобных решений или подтолкнете провайдера к его созданию. За спрос не бьют, как говорится.
Кому-то даже этот набор мер позволит существенно улучшить свое тестирование.
Заключение
А вообще, самое главное — это понимать описанные проблемы и иметь желание их разрешить. Тогда можно придумать множество альтернативных решений и подходов, которые будут работать еще лучше приведенных в статье для вашего конкретного случая. И тогда ваши тесты снова будут быстрыми, надежными и еще более полезными для вашей команды.
Автор: xpinjection