Как мы тестируем взаимодействие с Facebook
Вступление
Привет, хаброжитель! Уже довольно давно я хотел написать статью о том, как у нас в 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-инженер по автоматизации.