Вступление

Привет, хаброжитель! Уже довольно давно я хотел написать статью о том, как у нас в 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)


  1. Dreyk
    15.07.2016 16:37
    +1

    Шоты работают прям с живой базой данных? Или все же слепок с нее?


    1. nizkopal
      15.07.2016 17:16
      +2

      Да, шоты работают на живой базе.
      Суть тестирования на шоте — проверка работоспособности задачи в боевом окружении. Слепок боевой базы в проекте наших масштабов огромен, это работа не одного сервера.
      Если же взять только кусок базы, то проверка почти не будет отличатся от проверки на девеле.


      1. Dreyk
        15.07.2016 17:19
        +1

        такой подход, конечно, привлекателен, но я чет не решился бы тесты гонять на боевой базе


        1. nizkopal
          15.07.2016 17:28
          +2

          Ваши опасения понятны. Но в данном случае суть в том, что все пользователи, которые участвуют в тестах, тестовые. И QaApi может работать только с ними. Это все служит надежной защитой «от дурака».


          1. Tab10id
            16.07.2016 22:34

            Интересно узнать как вы разделяете тестовые данные от обычных и как реализовано ограничение прав доступа QaApi. Автоматизированные тесты запускаемые на реальной базе довольно привлекательны в некотором смысле, но количество тонкостей немного пугает.


        1. Rusan
          15.07.2016 17:30

          Тогда смысл шотов пропадал бы.


        1. LekaOleg
          15.07.2016 17:31
          -2

          А если сделать дамп боевой базы и развернуть её на тестовом сервере. Мы в компании так всегда делаем. Если конечно в этом есть потребность.


          1. Dreyk
            15.07.2016 17:34

            я так понимаю, там огромная база, которую просто так не сдампаешь в разумные сроки


          1. Rusan
            15.07.2016 17:35

            Вы реально представляете дамп базы Баду? Это далеко не один сервер… Это нехилая такая ферма…


            1. LekaOleg
              15.07.2016 17:51
              -1

              Я не спорю, но а если к примеру специально для тестирования развернуть и 1 раз в пару недель обновлять её? Пусть даже во время тестирования будет не самая свежая версия, но даже в пару недель будет уже неплохо. А по поводу серверов, то думаю у такого монстра как Баду есть парочку свободных ну или арендовать в дата центре? (я не знаком с их внутренней структурой, поэтому это только предположения и вопрос к более опытным людям) ^_^


              1. Rusan
                15.07.2016 18:00

                Еще раз задам вопрос — вы размеры кластера представляете? Получается что просто так держать такую ферму — это просто дико не выгодно по деньгам. Чисто экономически. Ну это как гуглу сказать — так вы бэкап своего индекса сделаейте и на нем эксперементируйте новые логики построения индекса вместо того чтобы на живой базе :).


                1. redmanmale
                  15.07.2016 18:13

                  Скорее всего, у гугла есть девелоперская копия индекса.


                  1. Rusan
                    15.07.2016 18:15

                    И у ФСБ и ЦРУ тоже. Попробуйте еще раз объем представить :).


              1. nizkopal
                15.07.2016 20:08

                Давайте я расскажу чуть подробнее. Badoo — это не несколько баз данных в mysql. Badoo — это два (почти три) полноценных датацентра. Это довольно сложная система репликации и шардинга. Плюс фото и мемкешы, система переводов, различные демоны и так далее. И все это дело каждый день улучшается, меняется, ломается и чинится. Бегут какие-то альтеры, какие-то селекты, тестовые инстансы демонов и АБ-тесты. Одним словом — разрабатывается.

                Следовательно, первое, что приходит в голову — для поддержания копии всей этой системы даже для одного шота точно так же потребовалось бы два-три датацентра. Второе — будет ли работоспособность нашей задачи в устаревшем окружении гарантировать ее работоспособность в будущем, в актуальной? Очевидный ответ — нет. И вся наша затея становится бессмысленной.

                Такие дела.


              1. youROCK
                16.07.2016 05:34

                Наши базы данных крутятся примерно на 300 серверах, причём это только пользовательские данные. Они конечно дампятся раз в какое-то время, но даже просто развернуть такой дамп (при условии, что вы уговорили начальство купить ещё 300 лишних серверов) будет требовать несколько суток.


  1. Tab10id
    16.07.2016 22:40

    А действительно ли вам нужны полноценные интеграционные тесты с facebook'ом, не рассматривали ли вы возможность замены facebook'а на соответствующий mock?


    1. mantyr
      17.07.2016 06:04

      Mock не поможет если API FB изменится и что-то сломается.


      1. Tab10id
        17.07.2016 09:20

        Да, если измениться FB API, полноценные тесты это конечно зафиксируют, но если такая ситуация действительно случится, то о том что что-то сломалось вы скорее узнаете не из тестов, а из массовых ошибок аутентификации/регистрации на проде=)
        Ну и кроме того, API никогда не должен изменяться. Как правило при доработках API выпускается его новая версия, которая работает параллельно текущей версии, а не заменяет ее. Ну а так как это все таки FB, то отвалившийся API это что-то крайне редкое и странное. Ну и добивает всю эту ситуацию то, что даже если вы обнаружили данную проблему с помощью тестов, то вы вряд ли сможете с этим что-то сделать, ну если только в техническую поддержку FB'ука написать…


        1. nizkopal
          17.07.2016 15:24

          Используя mock не будем ли мы тестировать тестовую логику?

          Методы, которые формируют запросы к сторонним сервисам, довольно сложные в плане зависимостей и количества условий. Если там что-то сломается, а мы замокаем ответ от FB, мы не поймаем проблему на стейджинге.

          А ловить отказ одного из основных способов регистрации/авторизации на продакшене по графикам того, сколько реальных пользователей не смогло попасть на сайт — попахивает письмом с заголовком «WTF??» всем нам на рабочую почту. :)


          1. Tab10id
            18.07.2016 00:20

            > Используя mock не будем ли мы тестировать тестовую логику?
            К сожалению, с mock'ами всегда имеется такая проблема=/ Мне вообще всегда довольно сложно дается ответ на вопрос использовать их или нет. Обычно для этого я задаю себе вопрос: «а что конкретно я тут тестирую??» и тогда решение дается проще. Если в вашем случае запросы к сервисам действительно имеют кучу параметров, то ваш текущий способ конечно же полностью оправдан.
            Но все же, в случае регистрации/аутентификации через FB, параметров должно быть минимум. Я правда никогда не работал с API FB, и вероятно сильно ошибаюсь, но для меня эта ситуация кажется странной.


        1. mantyr
          17.07.2016 21:08

          Вот пример: вы сделали некий функционал который использует 50% API фейсбука. Написали тесты, всё окей. Выкатили в продакшин… через неделю добавили новых функций по старым мокам (моками покрыли старую версию всю целиком), написали тесты, тесты прошли, выкатили в продакшин — новая часть функционала не сработала — появились жалобы.


          1. Tab10id
            18.07.2016 00:42

            > вы сделали некий функционал который использует 50% API фейсбука
            ИМХО, в таком случае уже появляется другая проблема — selenium для таких целей не очень подходит.
            Фактически мы проверяем что сторонний API работает так как мы ожидаем. В этом конечно нет ничего плохого, но в данном случае лично я бы проверял именно ответы API, а не работу системы с данным API.
            PS: Я просто пытаюсь рассуждать, не сочтите что я с вами спорю.