| Автор: Игорь Любин Представьте себе такую ситуацию. Вы запилили мегакрутую фичу на странице сайта и через месяц решили оценить её эффективность. Начинаете считать — и понимаете, что своим релизом вы сломали метрики на странице: случайно удалили код, отправляющий важные события аналитики, или забыли покрыть новую фичу событиями. Знакомо?
 События — это действия пользователей на сайте, которые можно отслеживать: клики на кнопки, переходы и просмотры страниц. Когда пользователь совершает целевое действие, в систему аналитики отправляется событие. В итоге мы получаем отчёт о поведении пользователей на конкретной странице сайта.
 
 Если события приходят некорректно, отчёт будет недостоверным.
 
 Тестирование всех событий продуктовой аналитики перед каждым релизом обычно отнимает много времени. В этой статье я расскажу, как автоматизировать этот процесс.
 
 Меня зовут Игорь Любин, я занимаюсь тестированием 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. Перехваты запросов. Проверки в конечной системе.  У каждого подхода есть свои преимущества и недостатки. Поэтому правильное решение — использовать комбинацию подходов.
 Мы запускаем автотесты по двум сценариям:
 
 
во время релизов сайта и бекендов — это позволяет находить ошибки до выхода новой функциональности и предотвращать возможные потери; регулярно по расписанию — что позволяет отлавливать ошибки в течение дня. Все это помогает нам поддерживать в рабочем состоянии нашу систему аналитики и предоставлять внутренним заказчикам точные и свежие данные. Обсудить в форуме |