Вступление
Привет, хаброжитель! Уже довольно давно я хотел написать статью о том, как у нас в Badoo устроена автоматизация тестирования. Хотелось написать о чем-то интересном и, в то же время, полезном. Поделиться опытом, который можно было бы легко интегрировать почти в любую систему. И вот, такая тема назрела…
Как многие из вас знают, Badoo — это социальная сеть, ориентированная на поиск новых друзей и знакомств. Одной из важнейших задач является верификация пользователя. Ведь всем нам хочется, чтобы привлекательная девушка, с которой у нас назначена встреча, не оказалась дядей Колей из Твери.
Для верификации пользователей у нас существует много различных способов. Некоторые из них довольно привычные, такие, как верификация по номеру телефона. Есть и более необычный — верификация по фотографии. Но самая простая и быстрая — верификация через социальные сети.
Такой способ верификации профиля возможен сразу на момент его создания — регистрация через социальную сеть. Во-первых, это быстро, всего один клик и никаких дополнительных манипуляций с телефоном или веб-камерой. Во-вторых, это удобно, так как при желании можно импортировать фотографии и информацию о себе, вместо того, чтобы вводить их вручную.
Сегодня я расскажу о том, как на Badoo устроена регистрация и верификация через Facebook и о том, как мы научили selenium-тесты ее проверять.
Итак, представим, что вы — новый пользователь сервиса Badoo. Вы заходите на сайт и видите вот такую форму регистрации. Заполнять все эти поля или кликнуть по кнопочке регистрации через Facebook? Для меня это — никогда не вопрос. Я бы заполнил все поля вручную и не стал бы привязывать свой аккаунт. Почему? Потому что я немного параноик, надеюсь, это не заразно. :)
На самом деле Badoo никогда не разместит какую-либо информацию из стороннего источника без согласия пользователя, так что спокойно кликаем по темно-синей кнопке и регистрируемся на сайте. Один клик, 10-15 секунд, и у нас есть свой верифицированный профиль на Badoo. Ура!
Как бы поступил настоящий QA-инженер после того, как создал профиль на сервисе так просто и быстро? Правильно, попробовал бы создать еще один. Как отреагирует сервис, если на тот же FB-аккаунт попробовать зарегистрировать еще один профиль?
Снова открываем страничку регистрации и кликаем по иконке FB. Ничего неожиданного не произошло, Badoo “узнало” FB-аккаунт и вместо регистрации сразу авторизовало. Все в порядке.
Первый selenium-тест на регистрацию
Теперь представим, что вы — QA-инженер в компании Badoo. Перед вами стоит задача — автоматизировать флоу регистрации и авторизации через FB аккаунт. На первый взгляд задача простая, вот что вам понадобится:
- аккаунт FB;
- локатор кнопочки FB на странице регистрации;
- метод, который ждет авторизационную cookie (чтобы убедиться, что тест залогинился на сайте);
- локатор sign out кнопочки, чтобы разлогиниться;
- метод, который ждет, когда авторизационная cookie пропадет.
После того, как были написаны необходимые методы, составляем сценарий:
- Открыть стартовую страницу;
- Кликнуть иконку Facebook’а;
- В открывшейся вкладке авторизоваться на Facebook;
- Дождаться авторизации на Badoo;
- Получить id пользователя (пусть будет first_user_id);
- Разлогиниться;
- Открыть стартовую страницу;
- Кликнуть иконку Facebook’a;
- Дождаться авторизации на Badoo;
- Получить id пользователя (пусть будет second_user_id);
- Убедиться, что first_user_id и second_user_id совпадают.
Итак, сценарий готов, вы запустили тест и он прошел. Все прекрасно. Самое время вставить котика в статью:
Коммитим код теста в ветку, кидаем задачу на ревью и идем пить кофе. Однако, не успели вы дойти до кухни, как приходит уведомление — задача не прошла ревью, тест не работает. Что-то пошло не так…
После перезапуска теста становится ясно, что проблема в следующем — у данного FB-аккаунта уже существует профиль на Badoo. Вместо регистрации тест сразу авторизовался. Делать нечего, надо удалять профиль по завершению теста. Слава Богу, у нас есть потрясающая штука — QaApi!
Несколько лет назад я рассказывал, как она интегрирована с нашими автотестами. Доклад назывался «Selenium тесты. От RC и одного пользователя к WebDriver, Page Object и пулу пользователей», найти его можно тут — habrahabr.ru/company/badoo/blog/216255.
Если коротко, это внутренний api, на который из теста можно отправить запрос и совершить некоторые манипуляции на стороне приложения. Вызывается он довольно просто:
QaApiHelper::deleteUser(user_id);
Само собой, QaApi умеет работать только с тестовыми пользователями и доступен только через внутреннюю сеть.
Когда тест научился удалять пользователя за собой, он стал работать стабильно и прекрасно. Но так было не долго.
Этапы тестирования Badoo
О том, какие этапы тестирования существуют в нашей компании, мы рассказываем практически на каждой конференции. Здесь я перечислю коротко те, которые интересны с точки зрения selenium-тестов:
- Тестирование на девел-окружении. Девел — это копия продакшена со своими базами и внутренними сервисами.
- Тестирование на шоте. Шот — это продакшен-окружение, доступное из внутренней сети по определенному урлу и являющееся мержем кода мастера и тестируемой задачи.
- Тестирование на стейджинге. Стейджинг, традиционно — это результат мержа ветки релиза и мастера.
- Тестирование на продакшене.
Изначально мы гоняли тесты на девел-окружении и стейджинге. Однако, со временем, пришли в выводу, что тесты нужно уметь гонять и на шотах. Причина довольно простая — девел не всегда идеально копирует продакшен, а ловить баг на стейджинге и откатывать задачу из релиза — плохо. Это значит, что задача в этот релиз уже не попадет и уедет позже запланированного.
Второй selenium-тест на регистрацию
Вернемся к нашему тесту. Представим, что вы — все тот же QA-инженер, перед которым теперь стоит задача научить тест регистрации работать параллельно на нескольких шотах и стейджинге.
Напомню, что шоты работают в боевом окружении, то есть база пользователей у них одна. Вполне очевидно, что в текущей реализации запускать тесты параллельно не получится. Если запустить два этих теста с разницей в пару секунд на разных шотах, то второй попытается создать профиль на Badoo тогда, когда его уже создал первый, и неизбежно сломается:
Как решить нашу проблему? Как сделать так, чтобы у теста всегда был свежий пользователь FB?
Сначала я попытался решить проблему минимально просто. Создал mysql-табличку, в которую внес несколько созданных руками FB-пользователей и проставил им статусы — свободны. Тест брал пользователя из этой таблички, меняя ему статус на “занят”. Если свободного пользователя не было, тест падал с соответствующим сообщением.
У этой системы было несколько очевидных минусов. Прежде всего, если одновременно запускалось слишком много инстансов теста, аккаунтов не хватало и взять их было неоткуда. Также тест мог по какой-то причине не освободить пользователя в конце (например если он был остановлен нажатием “Ctrl+C”). Ничто из этого не радовало по утрам, когда до релиза оставалось меньше часа.
Довольно быстро устав от нестабильных падений и неконтролируемых состояний FB-аккаунтов, я начал искать решение получше…
The Graph API
У Facebook есть замечательный api, который позволяет создавать тестовых пользователей и манипулировать ими — developers.facebook.com/docs/graph-api. Организована она довольно просто: мы формируем необходимый запрос и посылаем его на сервер FB, ответ возвращается в формате json.
Пример запроса, который зарегистрирует тестового пользователя с именем Alex:
https://graph.facebook.com/{APP_ID}/accounts/test-users?name=Alex&access_token={APP_ID}|{SECRET}
Application id и secret мы получаем, регистрируя наше приложение на FB (подробнее тут — developers.facebook.com/docs/facebook-login/overview).
Настоящий пул Facebook пользователей
Что ж, давайте создавать пользователей! :)
Внимательно изучив graph-api и его особенности, мы составили список нюансов:
- Количество регистраций на одно приложение ограничено. Цитата: “Для каждого приложения можно создать не более 2000 тестовых пользователей.”.
Вывод: нужно вести учет созданных пользователей. - Только что созданный тестовый пользователь может взаимодействовать только с единственным приложением. В данном случае приложение — это домен, на котором разместился сервис. В Badoo стейджинг и шоты находятся на разных доменах.
Вывод: ведя учет пользователей, необходимо их разделять по app id. - Пользователь регистрируется довольно медленно. В среднем от 2 до 5 секунд.
Вывод: удобнее иметь заранее созданного пользователя FB, чтобы тест не тратил время на его создание. - Тест должен иметь дело с тем аккаунтом, который наверняка не используется в каком-то еще тесте, чтобы избежать флуктуационных race condition.
Данный пункт важен в рамках описываемого здесь теста.
Итого: было бы круто иметь кастомный пул пользователей FB, который соответствовал бы нашим “хотелкам”. По сути это должна быть табличка, которая будет содержать следующую информацию:
- Id пользователя;
- Email;
- Password;
- App id;
- App Secret;
- Состояние пользователя — занят тестом или нет;
- Timestamp создания пользователя.
В дополнение к этой табличке нам понадобилось несколько скриптов.
Первый ищет в нашем пуле пользователей, которые заняты более N минут и помечает их как свободных. Это сделано для того, чтобы пользователей можно было использовать не только в тесте (где можно прописать гарантированный анлок пользователя по завершении работы), но и для ручного тестирования.
Второй решает проблему долгого создания пользователя на стороне facebook. Выглядит это следующим образом:
Получение FB-пользователя мы обернули в специальный QaApi метод. Тест обращается к нему за свободным пользователем. Если такого нет, создается специальное задание. В рамках этого задания скрипт посылает curl-запрос к graph-api, дожидается ответа и записывает в табличку нового пользователя. Тест же получает ответ — “необходимо подождать”, закрывает коннект и делает еще одну попытку спустя несколько секунд. Таким образом мы решили две проблемы. Во-первых, логика работы с graph-api отделена от логики тестов. Во-вторых, тесты не держат долгие коннекты к сторонним сервисам, что существенно облегчает дебаг любых проблем, связанных с увеличением времени прохождения тестов.
Далее мы переписали необходимые тесты на новую систему получения FB аккаунтов и оставили тесты гоняться на ночь с нашем CI-сервером (мы используем Teamcity). К утру результат был готов. Создалось ровно столько пользователей, сколько было необходимо для использования в тестах.
У пула есть довольно удобный интерфейс методов. Он позволяет в любой момент времени получать соотношение свободных пользователей к общему на каждое приложение, получать количество созданных пользователей по дате. Это помогает контролировать равновесие пула при добавлении нового теста, использующего FB-аккаунты.
А есть ли такой удобный API у других социальных сетей?
Я интересовался у ребят из ВКонтакте:
И у ребят из Одноклассников:
Итог
На данный момент пул является неотъемлемой частью нашей системы. Вокруг него появились новые скрипты и новые методы. Благодаря гибкости и простоте систему удобно развивать и контролировать.
Немного о том, что получилось в итоге:
- Удобный инструмент для ручного и автоматизированного взаимодействия Badoo с Facebook;
- Более 20 уникальных тестов, использующих пул facebook-аккаунтов: регистрация/авторизация, верификация, загрузка фотографий с FB-аккаунта, поиск друзей на Badoo, шаринг наград и так далее;
- В пуле 9 различных приложений, использующих в общей сложности примерно 1.5к активных пользователей;
- QaApi-методы умеют создавать FB-аккаунты, делать их друзьями, заливать им фотографию, ассоциировать FB-аккаунты с нашими тестовыми пользователями;
- Система сама поддерживает необходимое количество FB-аккаунтов и чаще всего не требует никакого ручного вмешательства.
За более чем год использования api работал стабильно. Всего раз возникла проблема с токенами пользователей, но разработчики facebook пофиксили ее довольно быстро. Кому интересно подробнее — developers.facebook.com/bugs/1662068220677444.
В завершение хотелось бы поблагодарить наших ребят из разработки за их неоценимую помощь в создании всей этой системы. Вы — молодцы!
Спасибо за внимание!
Виталий Котов, QA-инженер по автоматизации.
Комментарии (22)
Tab10id
16.07.2016 22:40А действительно ли вам нужны полноценные интеграционные тесты с facebook'ом, не рассматривали ли вы возможность замены facebook'а на соответствующий mock?
mantyr
17.07.2016 06:04Mock не поможет если API FB изменится и что-то сломается.
Tab10id
17.07.2016 09:20Да, если измениться FB API, полноценные тесты это конечно зафиксируют, но если такая ситуация действительно случится, то о том что что-то сломалось вы скорее узнаете не из тестов, а из массовых ошибок аутентификации/регистрации на проде=)
Ну и кроме того, API никогда не должен изменяться. Как правило при доработках API выпускается его новая версия, которая работает параллельно текущей версии, а не заменяет ее. Ну а так как это все таки FB, то отвалившийся API это что-то крайне редкое и странное. Ну и добивает всю эту ситуацию то, что даже если вы обнаружили данную проблему с помощью тестов, то вы вряд ли сможете с этим что-то сделать, ну если только в техническую поддержку FB'ука написать…nizkopal
17.07.2016 15:24Используя mock не будем ли мы тестировать тестовую логику?
Методы, которые формируют запросы к сторонним сервисам, довольно сложные в плане зависимостей и количества условий. Если там что-то сломается, а мы замокаем ответ от FB, мы не поймаем проблему на стейджинге.
А ловить отказ одного из основных способов регистрации/авторизации на продакшене по графикам того, сколько реальных пользователей не смогло попасть на сайт — попахивает письмом с заголовком «WTF??» всем нам на рабочую почту. :)Tab10id
18.07.2016 00:20> Используя mock не будем ли мы тестировать тестовую логику?
К сожалению, с mock'ами всегда имеется такая проблема=/ Мне вообще всегда довольно сложно дается ответ на вопрос использовать их или нет. Обычно для этого я задаю себе вопрос: «а что конкретно я тут тестирую??» и тогда решение дается проще. Если в вашем случае запросы к сервисам действительно имеют кучу параметров, то ваш текущий способ конечно же полностью оправдан.
Но все же, в случае регистрации/аутентификации через FB, параметров должно быть минимум. Я правда никогда не работал с API FB, и вероятно сильно ошибаюсь, но для меня эта ситуация кажется странной.
mantyr
17.07.2016 21:08Вот пример: вы сделали некий функционал который использует 50% API фейсбука. Написали тесты, всё окей. Выкатили в продакшин… через неделю добавили новых функций по старым мокам (моками покрыли старую версию всю целиком), написали тесты, тесты прошли, выкатили в продакшин — новая часть функционала не сработала — появились жалобы.
Tab10id
18.07.2016 00:42> вы сделали некий функционал который использует 50% API фейсбука
ИМХО, в таком случае уже появляется другая проблема — selenium для таких целей не очень подходит.
Фактически мы проверяем что сторонний API работает так как мы ожидаем. В этом конечно нет ничего плохого, но в данном случае лично я бы проверял именно ответы API, а не работу системы с данным API.
PS: Я просто пытаюсь рассуждать, не сочтите что я с вами спорю.
Dreyk
Шоты работают прям с живой базой данных? Или все же слепок с нее?
nizkopal
Да, шоты работают на живой базе.
Суть тестирования на шоте — проверка работоспособности задачи в боевом окружении. Слепок боевой базы в проекте наших масштабов огромен, это работа не одного сервера.
Если же взять только кусок базы, то проверка почти не будет отличатся от проверки на девеле.
Dreyk
такой подход, конечно, привлекателен, но я чет не решился бы тесты гонять на боевой базе
nizkopal
Ваши опасения понятны. Но в данном случае суть в том, что все пользователи, которые участвуют в тестах, тестовые. И QaApi может работать только с ними. Это все служит надежной защитой «от дурака».
Tab10id
Интересно узнать как вы разделяете тестовые данные от обычных и как реализовано ограничение прав доступа QaApi. Автоматизированные тесты запускаемые на реальной базе довольно привлекательны в некотором смысле, но количество тонкостей немного пугает.
Rusan
Тогда смысл шотов пропадал бы.
LekaOleg
А если сделать дамп боевой базы и развернуть её на тестовом сервере. Мы в компании так всегда делаем. Если конечно в этом есть потребность.
Dreyk
я так понимаю, там огромная база, которую просто так не сдампаешь в разумные сроки
Rusan
Вы реально представляете дамп базы Баду? Это далеко не один сервер… Это нехилая такая ферма…
LekaOleg
Я не спорю, но а если к примеру специально для тестирования развернуть и 1 раз в пару недель обновлять её? Пусть даже во время тестирования будет не самая свежая версия, но даже в пару недель будет уже неплохо. А по поводу серверов, то думаю у такого монстра как Баду есть парочку свободных ну или арендовать в дата центре? (я не знаком с их внутренней структурой, поэтому это только предположения и вопрос к более опытным людям) ^_^
Rusan
Еще раз задам вопрос — вы размеры кластера представляете? Получается что просто так держать такую ферму — это просто дико не выгодно по деньгам. Чисто экономически. Ну это как гуглу сказать — так вы бэкап своего индекса сделаейте и на нем эксперементируйте новые логики построения индекса вместо того чтобы на живой базе :).
redmanmale
Скорее всего, у гугла есть девелоперская копия индекса.
Rusan
И у ФСБ и ЦРУ тоже. Попробуйте еще раз объем представить :).
nizkopal
Давайте я расскажу чуть подробнее. Badoo — это не несколько баз данных в mysql. Badoo — это два (почти три) полноценных датацентра. Это довольно сложная система репликации и шардинга. Плюс фото и мемкешы, система переводов, различные демоны и так далее. И все это дело каждый день улучшается, меняется, ломается и чинится. Бегут какие-то альтеры, какие-то селекты, тестовые инстансы демонов и АБ-тесты. Одним словом — разрабатывается.
Следовательно, первое, что приходит в голову — для поддержания копии всей этой системы даже для одного шота точно так же потребовалось бы два-три датацентра. Второе — будет ли работоспособность нашей задачи в устаревшем окружении гарантировать ее работоспособность в будущем, в актуальной? Очевидный ответ — нет. И вся наша затея становится бессмысленной.
Такие дела.
youROCK
Наши базы данных крутятся примерно на 300 серверах, причём это только пользовательские данные. Они конечно дампятся раз в какое-то время, но даже просто развернуть такой дамп (при условии, что вы уговорили начальство купить ещё 300 лишних серверов) будет требовать несколько суток.