API для QA: тестируем фичи без доступа к коду +25


AliExpress RU&CIS

Многие фичи приложения невозможно быстро протестировать, не меняя исходный код. Представьте типичную задачу, с которой может столкнуться каждый разработчик: через три дня после регистрации пользователю нужно предложить купить премиум-доступ к продукту со скидкой. Чтобы проверить, работает ли это промо, нужно зарегистрировать тестового пользователя и подождать три дня до появления заветного окошка.  

Очевидно, что разработчик не будет столько ждать. Он просто сменит в коде значение константы, которая задаёт временной интервал, с трёх дней на 20 секунд — и получит результат почти мгновенно.  

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

Разумеется, для воспроизведения кейса менять значение прямо на стейджинге или продакшене невозможно. Можно было бы решить эту проблему, переместив значение в базу, но и в таком случае возникли бы трудности:

  • существенно усложнился бы код;

  • пришлось бы давать права на update базы всем заинтересованным лицам (и следить за этим), а также увеличилась бы вероятность человеческой ошибки при работе с базой, что может иметь самые разные последствия;

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

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

Мы не можем позволить себе постоянно тратить время, силы и ресурсы на решение подобных проблем при воспроизведении новой функциональности. Поэтому мы начали искать способ влиять на параметры системы, не меняя исходный код. Этот способ должен быть:

  • простым для пользователя: его будут использовать все инженеры вне зависимости от специализации; 

  • доступен для тестов различных типов: Calabash, Selenium, функциональных тестов;

  • простым для разработчика: реализация фичи не должна становиться дороже из-за внедрения возможности влиять на её параметры;

  • безопасным: как бы этот способ ни работал, это не должно повлиять на реальных пользователей на продакшене;

  • легкодокументируемым.

В Badoo такой инструмент называется QAAPI. По названию можно понять, что изначально он создавался для помощи тестировщикам, но оказался настолько мощным и многогранным, что сегодня без него не может представить свою работу ни один сотрудник, участвующий в процессах создания новых и поддержки существующих фич. 

В этой статье я расскажу о концепции QAAPI и о том, как она реализована в Badoo.

Использование QAAPI

QAAPI представляет собой набор вызываемых по HTTP и возвращающих JSON-ответ методов. Такой формат подходит всем: человек может вызывать методы из браузера, в то время как в каждой разновидности тестов (Calabash, Selenium и пр.) найдётся способ сделать HTTP-запрос. Аналогично и с JSON-ответом: он одинаково легко читается как человеком, так и тестами.

Для пользователя QAAPI решение проблемы с показом промо после регистрации выглядело бы так: есть метод SetPromoTimeOffset и соответствующий URL, принимающий в качестве GET-параметра количество секунд, по прошествии которого с момента регистрации необходимо показать промо, а также ID пользователя, для которого будет использоваться новое значение. К примеру:

/SetPromoTimeOffset?seconds=20&userid=12345

Запрос возвращает лаконичный ответ:

{ “success” : true }

И спустя 20 секунд после регистрации пользователю с ID 12345 показывается промо. 

Как же реализовать такой метод?

Создание QAAPI-метода

Каждый QAAPI-метод включает в себя три составляющие:

  • описание;

  • входные параметры;

  • тело метода с логикой.

Все параметры QAAPI строго типизированы и описываются отдельными классами с собственной валидацией. Они могут быть простыми, как Boolean или DateTime, и специфическими, как, например, user_id или session_id. 

Класс метода SetPromoTimeOffset выглядел бы следующим образом:

class SetPromoTimeOffset extends \QAAPI\Methods\AbstractMethod
{
    public function getDescription() : string
    {
        return <<<Description
Sets a time offset in seconds between the user's registration date and the promo's showing
Description;
    }

    public function getParamsConfig() : array
    {
        return [
            'user_id' => \QAAPI\Params\UserId::create(),
            'seconds' => \QAAPI\Params\PositiveInteger::create()->setDescription('Offset in seconds'),
        ];
    }

    public function run() : \QAAPI\Models\Output
    {
        // logic here

        return \QAAPI\Models\Output::success();
    }
}

Принцип работы метода зависит от его назначения. Подавляющую часть методов можно отнести к одной из трёх категорий:

  • получение данных;

  • изменение данных;

  • изменение логики сервера.

Первые две категории подразумевают простое взаимодействие с базой. Для изменения логики сервера необходимо как-то пометить нужного пользователя и сохранить для него необходимые значения.

Безопасность QAAPI

Безопасность QAAPI реализована на трёх уровнях. Первый — полный контроль разработчиков над тем, что делает метод, и скрытие реализации от конечного пользователя. Нет необходимости давать кому-либо права на базу и следить за этим. Сама система QAAPI требует авторизации, которая у нас реализована при помощи Google-авторизации для инженеров и секретного токена в хедере для тестов.

Второй уровень безопасности — это сетевое ограничение доступа к QAAPI: им можно пользоваться либо из офиса, либо при помощи VPN.

И третий уровень — принципиальное решение, позволяющее QAAPI работать исключительно с пользователями, помеченными как тестовые. Это избавляет нас от рисков что-то сломать для реальных юзеров, а также нарушить какие-то правила работы с персональными данными, к примеру GDPR.

Документация

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

Вот так выглядит наш новый метод:

Пример автоматически сгенерированной документации
Пример автоматически сгенерированной документации

Таким же образом мы легко можем генерировать и классы-клиенты для всевозможных тестов (Calabash, Selenium и пр.).

QAAPI в Badoo и Bumble

Области применения QAAPI, как показывает наш опыт, ограничиваются лишь креативностью разработчика. Его можно использовать в различных ситуациях:

— для регистрации тестового пользователя, когда нам нужно задать определенные пол, возраст, увлечения, количество и тип фотографий;

— для обмена сообщений между пользователями: создаём второго юзера с нужными параметрами и отправляем сообщение от его имени;

— при активации сервисов, воспроизведения флоу верификации пользователя, оплаты и так далее.

Методы можно комбинировать, вызывая один из тела другого. Это не делает вызов более дорогим: внутренние взаимодействия методов осуществляются локально, а не по HTTP.

Также было бы большим упущением, если бы часть нашей команды, которая не использует PHP, была лишена всей выразительности QAAPI и вынуждена довольствоваться только готовыми методами. Для неё мы создали вспомогательный инструмент, позволяющий писать сценарии на Lua и вызывать в них QAAPI-методы в любых сочетаниях. 

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

Заключение

Концепция QAAPI очень проста в реализации. Её можно использовать в любом проекте, где необходимо воспроизводить сложные кейсы и покрывать их тестами.

Нам QAAPI даёт массу преимуществ:

  • подготавливает данные для автоматического и ручного тестирования;

  • обеспечивает быстрое и безопасное получение данных о состоянии пользователя/фичи;

  • становится точкой входа для изучения работы функциональности (например, если метод включает какой-нибудь сервис, из тела метода легко перейти в код самого сервиса);

  • сценарии фактически являются документацией фич со сложным флоу.

Создание простого метода обычно не занимает больше пары минут, при этом QAAPI помогает сэкономить колоссальное количество времени. Помимо очевидных преимуществ, правильно написанный метод или сценарий позволяет абстрагироваться от деталей фичи и сфокусироваться на решении задачи.

QAAPI используется в Badoo с 2013 года, и сегодня представить нашу жизнь без него практически невозможно. При этом поддержка инструмента нам практически ничего не стоит: документация и классы-клиенты для тестов генерируются автоматически, каждый метод самостоятелен и не зависит от других. 

Мы часто упоминаем QAAPI, но ещё ни разу не рассказывали о нём подробнее в статьях. Надеемся, этот рассказ вдохновит вас создать подобный инструмент или поделиться с сообществом другими своими необычными решениями.

Теги:




Комментарии (5):

  1. DmitryKoterov
    /#22329992

    Данные-то где хранятся? Вот эти самые промо-секунды? Неужели отдельное поле в базе или поле в юзере? И так для всех тысяч эндпоинтов — везде по-разному?

    • SergeyRyabko
      /#22330954

      Такие временные данные можно хранить, например, в сессии юзера или в мемкеше

      • pbatanov
        /#22331134

        Если отбросить в сторону то, что база не очень быстрая, то поинт с базой был в том, что там значение одно на всех пользователей. Получается что вполне себе можно хранить на каждого пользователя свое значение и в бд (раз можно в мемкэше). Второй поинт был о том, что нужно следить за доступами. Для этого фактически вы написали свой API, через который и следите за правами. но с тем же успехом, опять же, можно выдавать права на одну конкретную таблицу в БД конкретным людям.

        Я не то чтобы набрасываю, идея клевая, на больших масштабах и если вернуть тезис про тормозную БД — очень даже интересная. Просто я о том, что вводные о том, зачем это было сделано выглядят притянутыми за уши. Звучит так: «У нас были вот такие проблемы со старым подходом. Но мы не стали их решать, а написали новую систему, где учли их заранее».

        • SergeyRyabko
          /#22331832

          Решение через базу имеет несколько недостатков:

          • если для каждого такого значения (или метода) заводить отдельную колонку, то для каждого метода нужно будет делать alter. Это существенно усложнит создание новых методов
          • если хранить для каждого пользователя данные в одном поле, например, в json, то появляется необходимость помнить или искать нужные ключи. К тому же, менять такие данные вручную очень неудобно
          • нужно не забывать чистить такую базу от данных удаленных пользователей

          Помимо этого QAAPI должен уметь работать с базой пользователей, чтобы выдавать или изменять их данные. Выдавать доступ к такой базе мы точно не можем, так как помимо тестовых пользователей там хранятся и реальные

          • pbatanov
            /#22332546

            Ничего не мешает хранить данные в формате «пользователь-ключ=>значение» (три колонки). И альтеры делать не надо и индексы работают и чистить легко и работать удобно. В мемкэше, полагаю, вы данные храните примерно так же, только там можно настроить какой-нибудь expire (который в БД можно реализовать тоже).

            > Выдавать доступ к такой базе мы точно не можем, так как помимо тестовых пользователей там хранятся и реальные
            Ну, в таком случае, да — надо писать свой интерфейс. При этом как реализовано хранилище опций, кажется, не сильно принципиально.

            Я еще раз повторю. Решение клевое. Вводные — нет. У какой-нибудь конторы с пятком разрабов и сервисов на тысячу пользователей в час вполне можно было бы обойтись и опциями в базе, все бы работало огонь.

            Можно было бы решить эту проблему, переместив значение в базу, но и в таком случае возникли бы трудности:


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