Привет, Хабр.
Меня зовут Сергей Вертепов, я старший бэкенд-инженер.
Это краткая обзорная статья о том, как мы тестировали монолитное приложение Авито, и что изменилось с переходом на микросервисную архитектуру.
Тестирование в эпоху домашнего обслуживания
Изначально приложение Авито было монолитным.Справедливости ради надо сказать, что наш монолит пока довольно большой, но микросервисов становится все больше.
Выше приведена схема того, как выглядит монолитное приложение.
У нас есть пользователь, уровень представления, бизнес-уровень, уровень данных и база данных, из которой мы их получаем.
Когда у нас был большой-большой монолит, объектом тестирования было приложение с бэкендом на PHP, фронтендом на Twig и совсем немного на React. Классическая пирамида тестирования монолитного приложения выглядит так:
- Множество модульных тестов.
- Чуть меньше интеграционных тестов.
- Еще меньше сквозных тестов.
- Вдобавок ко всему непостижимое количество ручных тестов.
Расскажу, как мы пытались прийти к такой пирамиде, и какая у нас была инфраструктура.
У нас была собственная тестовая среда на PHP с PHPUnit под капотом.
У нас также есть собственная система генерации тестовых данных.
Это менеджер ресурсов, который позволяет создавать любой необходимый запрошенный ресурс для тестирования.
Например, пользователь с деньгами, с рекламой в определенной категории на Авито, пользователь с определенным статусом.
Система просмотра отчетов также является самописной.
В дополнение к нему мы написали собственную jsonwire-сетку.
Grid — это система оркестрации узлов селена, которая позволяет поднимать узел селена по требованию.
Вы можете узнать больше о Grid, о том, как мы его разрабатывали и какие технологии использовали.
из доклада моего коллеги Михаила Подцерковского c Гейзенбаг 2018 Еще у нас есть свой selenium-maper. Мы написали собственную библиотеку, позволяющую выполнять запросы по протоколу jsonwire. Наш конвейер CI выглядел следующим образом: происходило какое-то событие CI, скажем, например, это была передача в репозиторий.
Артефакт был собран в CI для запуска тестов.
Самописная система параллельного запуска тестов разобрала артефакт и начала запускать тесты на куче разных нод.
В качестве тестового приложения мы использовали PHP-реализацию Selenide — полный порт Java. Но в процессе мы от него отказались, потому что Selenide было сложно поддерживать, сам он больше не развивался.
Мы написали собственный, более легкий PowerUI, вокруг которого построили архитектуру тестовых приложений с настраиваемыми сопоставителями, селекторами и так далее.
Этот продукт значительно упростил для нас тестирование и составление отчетов.
Соответственно, PowerUI затем вошел в узел селена через jsonwire-grid и выполнил необходимые действия.
Само наше тестовое приложение дополнительно шло к менеджеру ресурсов для формирования тестовых данных, а затем отправляло данные в нашу систему просмотра отчетов — Test Report System. В целом мы жили хорошо в этой парадигме.
Поначалу релизы большого монолитного PHP-приложения были раз в день, затем их количество выросло до трёх, а впоследствии и до шести.
У нас было несколько тысяч E2E-тестов с высоким покрытием, и они были довольно легкими.
В среднем они занимали около минуты, за редким исключением.
Тест, в котором проверялась огромная часть бизнес-логики, мог занять две-три минуты.
У нас было минимум ручной регрессии и минимум ошибок в продакшене.
Тестирование в микросервисной архитектуре
Со временем мы стали перейти на микросервисную архитектуру .Его основными преимуществами являются масштабируемость, скорость предоставления функций и отказоустойчивость.
С монолитом пирамида тестирования у нас не сложилась.
Вместо этого произошло «мороженое» тестирования.
С чем это было связано? Благодаря развитой инфраструктуре E2E-тесты проходили достаточно быстро и не причиняли особых хлопот. Поэтому мы сделали основной акцент именно на них.
Мы могли бы даже пренебречь модульными тестами.
С появлением микросервисной архитектуры этот подход больше не работал.
Огромная часть бизнес-логики ушла в отдельные сервисы, и их становилось все больше.
По состоянию на 2020 год у нас около 2,5 тысяч различных репозиториев.
В данном случае, когда мы запускали E2E-тесты, какой-то сервис мог, например, внезапно перестать отвечать.
Если он отваливался, то все тесты, которые шли на этот сервис и блокировались на слияние, тоже начинали проваливаться.
Соответственно, наше время выхода на рынок просто сократилось, так как люди не могли слиться из-за падающих тестов.
Мы были вынуждены сидеть и ждать, пока придет владелец того или иного сервиса, разберется в чем дело, перекатит его или разберется с проблемами.
После этого мы реализовали кармические тесты.
Это очень простой механизм.
Работает на основе самописной системы отчетности, имеющей все необходимые исторические данные.
Карма проверяет, что тест не пройден, а затем проверяет, возникает ли подобная ошибка при запуске тестов в других ветках.
Ошибка — это хеш трассировки.
Мы берем полную трассировку, хэшируем ее и сохраняем.
Если мы видим, что ошибка с тем же хешем возникает еще на трех ветках, мы понимаем, что проблема не в той ветке, на которой выполняются тесты, а носит общий характер.
Если ошибка общая и не связана с конкретной веткой, то разрешаем заморозить эту ветку.
Да, таким образом мы маскируем проблемы, но, тем не менее, такое решение значительно упростило жизнь разработчикам.
Если разработчик пропустил ошибку, нам остается процесс развертывания.
В деплое уже никакая карма не работает, все утверждается вручную тестировщиками и разработчиками, то есть мы напрямую смотрим, что произошло и как произошло.
Если мы обнаружим проблемы, развертывание блокируется до тех пор, пока проблемы не будут решены и не будет сделано исправление.
Количество микросервисов растёт, а вот тестировщиков не так уж и много.
Как правило, на единицу продукции у нас имеется один ручной тестер.
Понятно, что самостоятельно полностью покрыть потребности всех разработчиков в команде довольно сложно.
В среднем это несколько фронтендеров, несколько бэкендеров, еще специалисты по автоматизации и так далее.
Чтобы решить эту проблему, мы начали внедрять методологию Agile Testing. Суть этой методологии в том, что мы предотвращаем ошибки, а не находим их.
Мы обсуждаем тестирование на странице «Уточнения бэклога продукта».
Мы сразу определяемся, как будем тестировать фичу: достаточно ли покрыть ее юнит-тестом и если юнит-теста достаточно, то какой это должен быть юнит-тест. Обсуждение происходит с тестировщиком, который определяет необходимость дополнительного ручного тестирования.
Как правило, модульного теста достаточно.
Плюс тестировщик может подсказать еще какие-то случаи, а также может предоставить чек-лист, на основе которого мы напишем необходимые модульные или функциональные тесты.
Наша разработка начинается с приемочных тестов, то есть мы всегда определяем тесты, которые будут приняты во время разработки.
Подробнее о переход на гибкое тестирование Об этом мне уже рассказала моя коллега Алена из соседнего подразделения.
В статье она пишет о внедрении методики на примере своей команды, но это касается всего Авито.
Но Agile-тестирование невозможно без Shift-left-тестов.
Основная суть подхода тестирования Shift-left — найти дефекты на ранней стадии, чтобы разработчик мог запустить необходимый тест в любой момент во время написания кода.
Он также обеспечивает непрерывное тестирование в CI и возможность автоматизировать что угодно.
Во время тестов с нажатой клавишей Shift-left мы разбиваем большие и тяжелые тесты на несколько маленьких, чтобы они выполнялись быстрее.
Мы разложили наши огромные E2E-тесты на компонентно-интерфейсные тесты, юнит-тесты, интеграционные тесты, функциональные тесты, то есть начали распределять E2E по пирамиде.
Раньше запуск тестов мог занимать 20-30 минут и требовал танцев с бубном.
Сейчас в любом микросервисе тесты прогоняются за 5-10 минут, и разработчик сразу знает, сломал он что-то или нет. Мы также внедрили методологию контрактного тестирования.
Аббревиатура «Тесты CDC» означает тесты, ориентированные на потребителя.
Когда мы пишем изменение кода в репозиторий, специально написанный брокер собирает всю необходимую информацию о том, какие написанные CDC-тесты существуют, а затем понимает, к какому сервису они относятся.
Сами тесты CDC пишутся на потребительской стороне сервиса.
Когда мы выкатываем продюсера, мы запускаем все написанные тесты, то есть проверяем, что производитель никоим образом не нарушает контракт. Рассказывал тебе об этом подробнее в своем докладе Фрол Крючков , который просто продвинул эту идею.
Помогли ли нам тесты CDC? Нет, потому что есть проблема в том, что сами потребители не поддерживают свои тесты.
В результате тесты были нестабильными, а это означало, что наши производители не смогли вовремя выкатиться.
Пришлось идти и фиксировать тесты со стороны потребителей.
Плюс тесты все писали по-разному; для одной ручки может быть дюжина разных тестов, проверяющих одно и то же.
Это неудобно и отнимает много времени.
Поэтому мы отказались от идеи тестов CDC. Недавно мы внедрили PaaS. Наша команда архитекторов разработала очень удобный инструмент, благодаря которому вы сможете быстро развернуть сервис на шаблонах и сразу приступить к его разработке.
При этом вам не придется думать ни о базах данных, ни о миграторах и прочих инфраструктурных вещах.
Вы можете сразу же приступить к написанию кода и выполнению миграций.
Теперь служба потребителя и служба производителя взаимодействуют друг с другом через шлюз Api. На стороне Api Gateway осуществляется проверка контракта на основе кратких файлов.
Краткий файл — это очень простой структурированный файл, описывающий взаимодействие службы.
На стороне производителя есть краткий файл, в нем описано, как нам следует общаться с этим сервисом.
Потребитель с помощью копипаста берет нужный хэндл, нужную структуру, перетаскивает все это в свой сервис и на основе этого генерирует клиентов.
Соответственно, мы проверяем краткие файлы, чтобы убедиться, что все в порядке и что мы не нарушаем какое-либо сетевое взаимодействие.
Теперь такая валидация даже заблокирует слияние, если где-то будут нарушены наши контракты.
Мы просто проверяем контракты по инфраструктурной части.
Также мы представили такое понятие, как сервисная сетка.
Сервисная сетка — это когда рядом с сервисным кодом поднимается сайдкар, который проксирует все необходимые запросы.
Запрос не идет в сам сервис, его сначала принимает сайдкар, который пересылает нужные заголовки, проверяет, маршрутизирует маршруты и передает запрос сервису.
Служба передает запрос обратно в Sidecar и так далее по цепочке.
Вы можете узнать больше о сервисной сетке из репортажа Саши Лукьянченко с DevOpsConf 2019. В нем Саша рассказывает о том, как он разработал решение и как мы к нему пришли.
На основе коляски мы реализовали OpenTracing. Это технология, которая позволяет полностью отслеживать запросы от самого начала до финального обслуживания и видеть, какие были дампы и сколько времени занял запрос.
Это пользовательский интерфейс Jaeger. На снимке экрана показана трассировка запроса со временем выполнения и маршрутом.
Мы также провели тестирование постепенной деградации с использованием сервисной сетки.
Грациозное деградационное тестирование — это когда мы отключаем какой-то сервис и проверяем, как будет работать наше приложение.
Мы смотрим, не выдаст ли приложение какую-то ошибку или полностью выйдет из строя.
Мы не хотим такого развития событий, но поскольку микросервисов много, то и количество точек отказа тоже растёт. Этот тип тестирования позволяет проверить поведение всей системы при сбое одной из служб.
На скриншоте представлен боевой пример из одного из наших тестов во время прохождения.
Мы отключили сервис и проверили, что выдадим пользователю читаемое сообщение.
Все это работает благодаря сервисной сети Утилита Netramesh .
Достаточно ввести заголовок X-Route, наш сайдкар перехватывает запрос к сервису и перенаправляет его куда необходимо.
В конкретном случае мы перенаправили его в никуда, как будто сервис вышел из строя.
Мы могли бы указать ему вернуть 500-ю ошибку или тайм-аут. Netramesh все это умеет, проблема лишь в том, что ко всем запросам по протоколу DevTools необходимо добавлять необходимый заголовок.
Нижняя граница
Теперь для тестирования в микросервисной архитектуре мы используем:- Карма для E2E-тестов.
- Гибкая методология тестирования.
- PaaS с API-шлюзом.
- Сервисная сетка, благодаря которой работает тестирование OpenTracing и Graceful Degradation.
-
Обзор Apple Macbook Pro Mc373Ll/A
19 Oct, 24 -
Тайны Полуночи: Дьявол На Миссисипи
19 Oct, 24 -
Рукава Чашки
19 Oct, 24 -
Как Выбрать Большую Фотографию Для Сайта
19 Oct, 24 -
Между Космосом И Интернетом
19 Oct, 24 -
Краткое Введение В Javafx
19 Oct, 24 -
Сделай Сам Или Наглые Спамеры
19 Oct, 24