Конспект доклада «Монолит для сотен версий клиентов» (HL2018, Badoo, Владимир Янц)

    Продолжаю серию конспектов с HL2018. В проверке этого конспекта мне помогали ребята из Badoo (Владимир Янц vyants и Николай Крапивный), за что им большой спасибо. Надеюсь, это положительно сказалось на качестве донесения идеи доклада.

    image

    Особенности процесса разработки:


    Ответственность разработчика не заканчивается релизом бэкенда. Он отвечает до реализации на платформах.

    image

    Есть ручное тестирование, но клиент не готов к моменту релиза и выпускается с (непредсказуемой) задержкой. Мы чаще всего не в курсе когда клиенты начнут это имплементировать. Иногда (не часто) фичи начинают делать через большой срок. Поэтому тестировать руками тяжело и не все можно. Поэтому нужны автотесты.

    Тесты


    Unit тесты


    Пишутся на phpunit.

    Тестируют малую единицу. Не ходят ни в базу, ни в сервисы (не должны ни с чем взаимодействовать).

    Легаси до сих пор есть и усложняет процесс тестирование.

    Разработали библиотеку softMocks — перехватывает все include / require и подменяет на изменённый.

    Можно мотать любые методы: статические, приватные, финальные.
    Библиотека доступна в опен сорс.

    Проблема: softmocks расслабляют и дают писать не тестируемый код (и все аврно покрыть его тестами).

    Приняли правила:

    • Новый код должен быть легко тестируем phpunit
    • SoftMocks — крайний случай (старый код /долго / дорого / сложно)

    На эти правила смотрим на код ревью

    Качество тестов


    Мутационное тестирование


    • Берём код
    • Берём code coverage
    • Парсим код и применяем мутации (меняем + => -; true => false и тп.)
    • Для каждой мутации прогоняем сюит (набор) тестов.
    • Если тесты упали, то ок. Если нет — они недостаточно эффективны. Разбираемся, меняем / дописываем тесты.

    Есть готовые решения (Humbug, Infection), но они не подошли (не совместимы с softmocks, есть сложности с code coverage). Поэтому написали свое.

    Мутационное тестирование пока недоступном для ручного теста. Доступно для запуска в ручную, из консоли. Сейчас внедряем в CI pipeline, выстраиваем процесс. Результат будет на хабре.

    Интеграционные тесты


    Тестируем работу нескольких компонентов в связке; проверяем работу с базой и/или сервисами.

    Стандартный подход к тестированию БД (DBUnit):

    1. Поднимаем тестовую БД
    2. Заполняем ее
    3. Запускаем тест
    4. Очищаем БД

    Проблемы:

    • Нужно поддерживать datatables и dataset (актуальность содержимого БД)
    • Требуется время на подготовку бд
    • Параллельные запуски делают тесты нестабильными и порождают дедлоки

    Решение: библиотека DBMocks (своё решение)

    Принцип работы:

    • Методы драйверов dB перехватываются с помощью SoftMocks на setUp теста
    • Из запроса парсим db + table
    • В tmpfs создаются временные таблицы с такой же схемой
    • Все запросы ходят только во временные таблицы
    • На TearDown они удаляются

    Библиотека небольшая, но пока не запушена в опен сорс

    Результаты:

    • Тесты не могут повредить данные в оригинальных таблицах
    • Тесты изолированы друг от друга (можно запускать параллельно)
    • Тестируется совместимость запроса с версией MySQL

    API тесты


    • Имитируют клиентскую сессию
    • Умеют слать запросы к бэкенду
    • Бэкенд отвечает почти как реальному клиенту

    Обычно такие тесты требуют авторизованного пользователя. Его нужно создать перед тестом и удалить после. Это вносит дополнительные риски (репликация, фоновые задачи).

    Решение: Сделали пул тестовых пользователей. Научились их очищать.

    image

    Тестовые пользователя находятся в одном окружении с реальными, потому что devel != prod. Нужно изолировать тестовых и живых пользователей.

    Для изоляции добавили флаг is_test_user у пользователя. И эти пользователи также исключаются из аналитики и результатов a/b тестов.

    Можно сделать дешевле — отправить тестовых пользователей «в Антарктиду», где их никто не увидит (кроме пингвинов).

    QA API


    Инструмент для подготовки окружения в api тестах, по сути бекдор в бэкенде для быстрого изменения параметров пользователя / среды.

    • Хорошо документированные апи методы
    • Быстро и легко управляют данные
    • Пишут backend разработчики
    • Можно применять только к тестовым пользователям

    Позволяют изменить пользователю неизменяемые данные (например, дату регистрации).

    Требуется защита:

    • На уровне сети (доступно только из офисной сети)
    • С каждым запросом передаётся secret, валидность которого проверяется
    • Методы работают только с тестовыми пользователями

    Есть BugsBounty программа на HackerOne. Платят за найденные уязвимости. Один косяк с QA API нашли с ее помощью.

    Remote mocks


    Моки для удаленного бэкенда.

    Работают на Базе soft mocks. Тест просит backend инициализировать для сессии mock. При получении запроса бэкенд проверяет список моков для сессии и применяет их при помощи SoftMocks.

    Пример теста:

    image

    API тесты слишком удобны. Есть соблазн писать их вместо Unit. Но API тесты сильно медленее.

    Приняли набор правил:

    • Цель АПИ тестов — тестировать протокол и правильность интеграции
    • Допустимо проверять сложные флоу
    • Нельзя тестировать мелкую вариативность
    • На code review проверяем тесты тоже.

    Ui тесты


    Не пишет бэкенд команда.

    Фича покрывается Ui тестами когда стабилизируется.
    Используется selenium для веб. Для мобильных calabash.

    Прогон тестов


    100 000 юнит тестов. 6 000 интеграционных, 14 000 апи тестов.
    В 1 поток время 40 мин / 90 мин / 10 часов.

    Сделали TestCloud — облако для запуска тестов.

    image

    Распределение теста между потоками:

    • Можно поровну (плохо, все тесты разные, получается неравные по времени части)
    • Запустить несколько потоков и последовательно скармливать тесты phpunit по одному (накладные расходы на инициализацию. Долго!)

    Решение:

    • Сбор статистики по времени прогона.
    • Компоновка тестов так, чтобы чанк прогонялся не больше 30 секунд

    Проблема с апи тестами — долго, много ресурсов и не дают выполнится другим.

    Для решения разбили cloud на 2 части:

    1. Запускает только быстрые тесты
    2. Запускает оба типа тестов.

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

    • Unit — 1 мин
    • Интеграционные — 5 мин
    • API — 15 минут.

    Прогон по code coverage


    Какие тесты выполнять? Покажет code coverage.

    1. Получаем branch diff
    2. Формируем список изменённых файлов
    3. Получаем список тестов для этих файлов
    4. запускаем прогон сюита только из этих тестов.

    Coverage формируется раз в сутки, ночью, для master-ветки. Результаты (diff) складываем в базу.

    Плюсы:

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

    Минусы:

    • Релиз бэкенд 2 раза в день. После обеда coverage менее актуален (но при выкате била всегда гоняем полный сьют).
    • Апи тесты генерируют обширный coverage. Для них этот подход не дает большой экономии.

    Если разработчику нужно сразу посмотреть code-coverage, то есть тулза которую можно запустить в консоле и сразу получить свежую метрику по coverege конкретного файла/компонента.
    Считается хитро: берется данные по coverege мастера, добавляются все измененную тесты, получается маленький сьют по которому уже и считается покрытие.

    Итоги


    • Нужны все уровни тестов
    • Количество != качество. Делайте Code review и мутационное тестирование
    • Изолируйте тестовых пользователей от реальных
    • Бэкдоры в бэкенде упрощают и ускоряют написание тестов
    • Собирайте статистику по тестам.

    Ссылки


    • +24
    • 3,9k
    • 3
    Поделиться публикацией

    Комментарии 3

      –2
      На 3 вещи можно смотреть вечно: огонь, воду и как badoo изобретают велосипеды :)
        +2
        Вот как бы нам внедрить ваши практики… особенно «Ответственность разработчика не заканчивается релизом бэкенда. Он отвечает до реализации на платформах. „

        Позицию разрабов “у меня код запустился, а на проде у вас с инфраструктурой проблемы… мой код работает.» без рукопрекладства решить ну ни как не удаётся…

        Боль-печаль…

        Ps. Спасибо за доклад.
          +2
          В хорошем направлении движетесь. Может, когда-нибудь, у вас и DI появится. Не исключаю, что самописный бо ни одна из имеющихся реализаций вам не подойдёт.
          Однако, если когда-нибудь вы дойдёте до того, чтобы внедрять DI, то, плиз, не используйте его как сервис-локатор, но и не скатывайтесь в «инжектить всё». Некоторые вещи стоит использовать через классические статические фабрики.

          Доклад хороший. Спасибо.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое