Автор: Игорь Любин Представьте себе такую ситуацию. Вы запилили мегакрутую фичу на странице сайта и через месяц решили оценить её эффективность. Начинаете считать — и понимаете, что своим релизом вы сломали метрики на странице: случайно удалили код, отправляющий важные события аналитики, или забыли покрыть новую фичу событиями. Знакомо?
События — это действия пользователей на сайте, которые можно отслеживать: клики на кнопки, переходы и просмотры страниц. Когда пользователь совершает целевое действие, в систему аналитики отправляется событие. В итоге мы получаем отчёт о поведении пользователей на конкретной странице сайта.
Если события приходят некорректно, отчёт будет недостоверным.
Тестирование всех событий продуктовой аналитики перед каждым релизом обычно отнимает много времени. В этой статье я расскажу, как автоматизировать этот процесс.
Меня зовут Игорь Любин, я занимаюсь тестированием 14 лет. Сейчас работаю в Ozon департаменте Buyer Experience (BX, «Опыт пользователя»): отвечаю за тестирование десктопной и мобильной версий сайта, а также их бэкенда. Наш сайт представляет собой совокупность разных страниц, каждую из которых делает отдельная команда (мы называем их вертикалями). Я работал в нескольких вертикалях: «Каталог», «Корзина», «Личный кабинет». Сейчас я в команде платформы и помогаю всем вертикалям.
Обо мне лучше всего говорит скриншот из GitLab:
Раскрашенные квадратики показывают, когда я пишу код, комментирую и создаю merge requests. Ранее я писал тесты и фреймворки на Python, С#, Java, Ruby и PHP, а сейчас в основном использую TypeScript и Go. Все примеры в статье будут на TypeScript, но аналогичные подходы можно применять и в других языках.
Стек технологийВот что мы используем в Ozon:
Бэкенд — это все микросервисы, половина из которых написана на Go, остальные — на C#. Всё это завернуто в Docker и крутится в Kubernetes. Код хранится в GitLab, там же реализован CI/CD.
Фронтенд — большой монорепозиторий сайта на TypeScript. Мы тестируем его с помощью фреймворка WebdriverIO. Это полноценный фреймворк тестирования, который может управлять Selenium или Chrome DevTools Protocol.
Также у нас есть две тестовые площадки, на которых крутится Aerokube Moon, решение для Kubernetes. А работу сайта в разных экзотических браузерах мы тестируем с помощью BrowserStack.
Основные сценарииДля примера я выбрал две странички: каталог и корзину.
Основные сценарии в каталоге — это просмотр товаров на так называемых плитках, на которые можно кликнуть и перейти на страницу с подробной информацией.
Также можно прямо на плитке нажать кнопку «В корзину» — и товар попадёт в корзину.
В корзине основной сценарий — нажатие на зелёную кнопку «Перейти к оформлению», которая ведёт на чекаут.
Сайт поделён на блоки — виджеты. Данные подтягиваются из бэкенда, а фронтенд их отображает в виде блоков-виджетов. Те из них, которые показаны пользователю, отправляют в аналитические системы событие view. Ещё есть событие click, которое отправляется, когда пользователь нажимает на кнопки и ссылки на виджетах.
Есть и другие события, но я их опущу.
АналитикаМы используем две системы аналитики: Google Analytics и собственную разработку Ozon Tracker. При этом наш трекер покрывает примерно 90% бизнес-задач. Обе системы нужно проверять.
- Аналитика нужна представителям бизнеса, менеджерам и аналитикам, чтобы рассчитывать получаемую компанией прибыль, изучать спрос на конкретные товары и расширять ассортимент, предлагать покупателям более конкурентные цены.
- Аналитика нужна сервисам. Например, поисковому движку — для улучшения поисковой выдачи или рекомендательным полкам. Также аналитику используют сервисы антифрода, выявляющие ботов.
Поэтому, если в аналитике будут какие-то проблемы, наши системы будут плохо работать, а менеджеры аналитики не смогут доверять данным и предлагать пользователям лучшие условия.
Проблемы с событиямиПроблемы с событиями бывают разные, порой даже непредсказуемые. Приведу несколько примеров.
События не приходят
Голубая вертикальная линия на этом графике — это время релиза. После неё одна из линий идёт вниз. Это события добавления товаров в корзину, которые после релиза перестали получать аналитические системы.
Приходят не все события
На рекомендательной полке мы показываем товары. В этом примере на полке шесть плиток, а в аналитическую систему пришло только четыре события.
Битые события
В корзине находится товар. Событие отправляется, но оно битое: у него нет ID, пустое имя. Непонятно нашим коллегам из бизнеса, что пользователь положил в корзину.
Лишние событияБывает так, что событий приходит больше, чем должно быть, потому что они дублируются. У нас был случай, когда на страницу добавили два одинаковых виджета разных версий, которые отравляли в аналитическую систему события. Таким образом, одно событие приходило два раза.
События приходят волнами
Причина волн на этом графике в том, что один из сервисов бэкенда стал долго отвечать, — и наша система аналитики просто выкидывала запросы к нему, не дожидаясь ответа. Так мы теряли часть событий.
События приходят с задержкойЭто график за три дня: оранжевая линия показывает, что событий стало больше, причём часть из них пришла ночью, то есть события где-то задерживаются.
Источники проблемЕсли обобщить весь набор проблем, то можно выявить два их источника:
- Ошибки в релизах. Мы релизим сайт в среднем десять раз в день, а микросервисы бэкенда — суммарно 50 раз в день.
- Инциденты эксплуатации, когда время от времени что-нибудь тормозит или ломается.
Все эти сбои в получении метрик нужно уметь выявлять.
Тестирование аналитикиБраузерные тесты мы делим на три группы в порядке приоритета:
- Критичные тесты. Это функциональные тесты, которые обеспечивают работоспособность сайта, в основном позитивные сценарии пользовательского поведения.
- Тесты аналитики, которая важна для других систем, в первую очередь для сервисов поиска, рекомендательных систем и сервисов антифрода.
- Тесты отдельных вертикалей.
Тестирование аналитики для нас очень важно. Проверять её можно тремя способами:
- Тестирование с использованием DataLayer.
- Перехват запросов.
- Проверка в конечной системе.
Рассмотрим их.
DataLayerC Google Analytics связано понятие специального контейнера для событий. Браузер, в котором мы совершаем действия, накапливает события и складывает их в этот контейнер DataLayer, откуда информация потом отправляется в Google Analytics.
В тесте мы выполняем действия в браузере, а проверки делаем в DataLayer.
Содержимое контейнера можно посмотреть в консоли браузера.
window.dataLayer
Есть плагин для Chrome Datalayer Checker, в котором тоже можно увидеть все события. Так можно вручную осуществлять проверку.
Для автоматической проверки пишем такой тест:
it('AddToCart', (): void => {
CatalogPage.openDirectlyCategory (Category.smartfony);
CatalogPage.waitTiles();
CatalogPage.ClickAddToCartOnTile(0);
AnalyticsHelper.waitEvent();
const events = Network.getDataLayer();
AnalyticsHelper.expectEventsEquals(etalon, events);
});
Порядок действий здесь следующий:
- Открываем категорию «Смартфоны».
- Ждём загрузки плиток с товарами.
- Нажимаем кнопку «Добавить в корзину» на плитке с индексом 0.
- Ждём событий.
- Достаём DataLayer.
- Сравниваем события с эталоном.
Функцию getDataLayer() легко реализовать в WebDriver, она вызывает JavaScript:
@step()
getDataLayer() {
return browser.execute(() => window.dataLayer);
}
В ней мы вызываем то же самое, что вручную вводили в консоли — window.dataLayer, — и получаем события, которые хотим проверить.
Подход с использованием DataLayer очень простой и быстрый: достаточно вызвать всего одну JS-команду. Но он применим только для тех аналитических систем, где есть некий контейнер для событий. Второй важный недостаток — не все события можно проверить с помощью DataLayer. Например, когда мы кликаем на плитку с товаром, то переходим на другую страницу с другим DataLayer и такой переход невозможно проверить. Поэтому в подобных случаях мы применяем механизм с перехватом запросов.
Перехват запросовВ тесте мы совершаем действия в браузере, он отправляет события в Google Analytics, а мы проверяем их, перехватывая запросы.
Как это делать в консоли? Во вкладке Network можно просматривать события collect. Там много непонятных на первый взгляд параметров, из которых мы выбираем нужные.
То же самое нужно сделать в коде: перехватить событие collect и отфильтровать запросы. Вот пример теста на клик, который мы не смогли бы реализовать в DataLayer:
it('Click', (): void => {
CatalogPage.openDirectlyCategory(Category.smartfony);
CatalogPage.waitTiles();
const events = Network.enableGaEventsInterception();
CatalogPage.clickTileWithIndex(0);
AnalyticsHelper.waitEvent();
AnalyticsHelper.expectEventsEquals(etalon, events);
});
После нажатия на плитку выполняется переход на другую страницу. Перехватчик будет сохранять события в переменную events.
В тестах мы можем выполнять как команды обычного Webdriver, то есть делать клики, так и команды CDP для перехвата запросов:
global.cdp.send('Network.enable');
global.cdp.send('Network.setRequestInterception', {
patterns: [
{
urlPattern: '*collect*',
},
],
});
global.cdp.on('Network.requestIntercepted', ({ request }) => {
…
});
Переменная request будет содержать перехваченный запрос. С ней можно дальше работать: фильтровать данные и делать проверки.
Этот способ позволяет проверить больше сценариев, чем DataLayer: добавляются клики и переходы на страницы. Но он не подходит для тех случаев, когда события зашифрованы или обфусцированы, так как мы не можем проверить их содержимое.
Проверка в конечной системеМы проверяем, попадают ли в базу отправляемые браузером события.
Так это выглядит в коде:
it('Add to cart', (): void => {
CatalogPage.openDirectlyCategory(Category.smartfony);
CatalogPage.waitTiles();
const sessionId = Browser.getSessionId();
CatalogPage.clickAddToCartOnTile(0);
AnalyticsHelper.waitEvent();
const events = QaApi.getEvents({ sessionId: sessionId });
AnalyticsHelper.expectEventsEquals(etalon, events);
});
Порядок действий:
- Открываем категорию «Смартфоны».
- Запоминаем сессию в браузере, с которой пользователь зашёл на сайт.
- Ждём появления плиток.
- Нажимаем кнопку «Добавить в корзину» на плитке с индексом 0.
- Опять ждём событий.
- Достаём событие по сессии.
- Сравниваем перехваченные события с эталоном.
Здесь мы достаём события с помощью вспомогательного сервиса QaAPI. Под капотом у него выполняется запрос к базе, так что обращение к сервису можно заменить обращением к базе.
Почему мы проверяем не через базу? Потому что тесты пишут много людей, а подключение к базе — это всегда какая-то одна учётная запись, которую очень удобно спрятать за сервисом, чтобы вызывать программно через клиент или проверять вручную с помощью Swagger.
Для ручной проверки у каждого нашего сервиса есть Swagger; сюда можно передать sessionId — и командой getEvents достать все события из ClickHouse.
Преимущество подхода в том, что проверки получаются более полными, более интеграционными, мы проверяем всю цепочку действий. Но для этого требуется больше времени — тесты становятся достаточно долгими.
Например, для Tracker у нас SLA равно пяти минутам, то есть событие гарантированно долетает до ClickHouse за пять минут. А вот для Google Analytics временной лаг составляет от четырёх часов до суток, поэтому такая проверка не годится.
Кроме того, не у всех инженеров есть доступ к конечной системе. Действовать надо осторожно, так что к Tracker мы обращаемся точечными выверенными запросами — иначе можно положить базу, ведь в аналитических системах очень много событий.
Проверка событийЭта проверка есть в каждом тесте в рассмотренных выше примерах:
AnalyticsHelper.expectEventsEquals(etalon, events);
Что скрыто за конструкцией сравнения с эталоном?
- Проверяем, есть ли интересующее нас событие в передаваемом массиве.
- Проверяем значимые поля: в основном ID товаров и пользователей, названия виджетов и категорий.
- Проверяем события на дубли.
Использование подходовТесты трекера мы делим на две группы:
- быстрые — с перехватом запросов — используем для проверки релизов, когда хотим проверить быстро и не задерживать разработчика, который катит релиз;
- медленные — с проверкой в конечной системе — используем для проверок по расписанию и с их помощью мониторим, что события в течение дня гарантированно долетают до базы данных.
Тесты Google Analytics тоже делим на две группы:
- DataLayer — для проверки действий на странице;
- перехваты запросов — для проверки переходов между страницами.
Решение проблемВыше я упоминал, что у нас два основных источника проблем с получением метрик: многочисленные релизы и инциденты эксплуатации.
Проблемы в релизах мы решаем встраиванием браузерных тестов в пайплайн.
Пайплайн production-сборки одного из сервисов бэкенда. Пайплайн сайта выглядит примерно так же, только тестов в разы больше.
В production мы используем технологию B/G deploy: после сборки сервисы выкладываются в так называемую синюю зону — пользователи их не видят. Пока сервисы в этой зоне, их можно тестировать. Мы делаем это с помощью браузерных тестов с наивысшим приоритетом. После их успешного прохождения можно пускать на сервисы трафик.
Вторая проблема — эксплуатация сайта. С ней гораздо сложнее, потому что не знаешь, в какой момент где и что откажет. Спасает запуск тестов по расписанию и мониторинг ими продакшена.
Сверху — график по Tracker, снизу — по Google Analytics. Слева — результаты тестирования аналитики, справа — данные из аналитических систем. Такие панели помогают наблюдать, как отправляются и поступают события. Благодаря этим графикам мы можем в течение дня оповещать вертикальные команды и реагировать на инциденты.
Оценка тестового покрытия по событиям аналитикиСобытия из аналитических систем могут быть полезны тестировщикам для оценки тестового покрытия. Вот пример покрытия тестами кликов по виджетам:
Голубая колонка — это виджеты на разных страницах сайта, зелёная — количество кликов по ним пользователей. В правой колонке показано количество тестов на каждый клик в каждом виджете.
Для разделения событий от пользователей и от тестов у нас встроена механика. Для каждой категории мы ставим свой namespace. Когда по сайту ходят пользователи, аналитические события отправляются с меткой namespace user, а когда тесты — с namespace test. По этим категориям мы строим таблицу со статистикой.
Приведу несколько курьёзных примеров, которые мы выловили с помощью этой таблицы.
Пользователи кликнули на популярный виджет 5 млн раз — и при этом он не покрыт тестами! Если виджет сломается из-за релиза, это заметит большое количество людей — для нас будут потери.
А вот пример с непопулярным виджетом. На него не идёт трафик, но зато мы его тестируем.
РезюмеВ статье рассмотрены три подхода, которые мы в Ozon применяем для тестирования аналитики:
- С использованием DataLayer.
- Перехваты запросов.
- Проверки в конечной системе.
У каждого подхода есть свои преимущества и недостатки. Поэтому правильное решение — использовать комбинацию подходов.
Мы запускаем автотесты по двум сценариям:
- во время релизов сайта и бекендов — это позволяет находить ошибки до выхода новой функциональности и предотвращать возможные потери;
- регулярно по расписанию — что позволяет отлавливать ошибки в течение дня.
Все это помогает нам поддерживать в рабочем состоянии нашу систему аналитики и предоставлять внутренним заказчикам точные и свежие данные. Обсудить в форуме |