Разделы портала

Онлайн-тренинги

.
Автоматическое тестирование аналитики в браузере
10.12.2021 00:00

Автор: Игорь Любин

Представьте себе такую ситуацию. Вы запилили мегакрутую фичу на странице сайта и через месяц решили оценить её эффективность. Начинаете считать — и понимаете, что своим релизом вы сломали метрики на странице: случайно удалили код, отправляющий важные события аналитики, или забыли покрыть новую фичу событиями. Знакомо?

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

Если события приходят некорректно, отчёт будет недостоверным.

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


Меня зовут Игорь Любин, я занимаюсь тестированием 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, пустое имя. Непонятно нашим коллегам из бизнеса, что пользователь положил в корзину. 

Лишние события

Бывает так, что событий приходит больше, чем должно быть, потому что они дублируются. У нас был случай, когда на страницу добавили два одинаковых виджета разных версий, которые отравляли в аналитическую систему события. Таким образом, одно событие приходило два раза.


События приходят волнами


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

События приходят с задержкой

Это график за три дня: оранжевая линия показывает, что событий стало больше, причём часть из них пришла ночью, то есть события где-то задерживаются.

Источники проблем

Если обобщить весь набор проблем, то можно выявить два их источника:

  1. Ошибки в релизах. Мы релизим сайт в среднем десять раз в день, а микросервисы бэкенда — суммарно 50 раз в день. 
  2. Инциденты эксплуатации, когда время от времени что-нибудь тормозит или ломается. 

Все эти сбои в получении метрик нужно уметь выявлять. 

Тестирование аналитики

Браузерные тесты мы делим на три группы в порядке приоритета:

  1. Критичные тесты. Это функциональные тесты, которые обеспечивают работоспособность сайта, в основном позитивные сценарии пользовательского поведения.
  2. Тесты аналитики, которая важна для других систем, в первую очередь для сервисов поиска, рекомендательных систем и сервисов антифрода.
  3. Тесты отдельных вертикалей.

Тестирование аналитики для нас очень важно. Проверять её можно тремя способами: 

  1. Тестирование с использованием DataLayer. 
  2. Перехват запросов.
  3. Проверка в конечной системе. 

Рассмотрим их. 

DataLayer

C 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);
});

Порядок действий здесь следующий:

  1. Открываем категорию «Смартфоны».
  2. Ждём загрузки плиток с товарами.
  3. Нажимаем кнопку «Добавить в корзину» на плитке с индексом 0. 
  4. Ждём событий. 
  5. Достаём DataLayer.
  6. Сравниваем события с эталоном. 

Функцию 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);
});

Порядок действий:

  1. Открываем категорию «Смартфоны». 
  2. Запоминаем сессию в браузере, с которой пользователь зашёл на сайт. 
  3. Ждём появления плиток.
  4. Нажимаем кнопку «Добавить в корзину» на плитке с индексом 0. 
  5. Опять ждём событий.
  6. Достаём событие по сессии.
  7. Сравниваем перехваченные события с эталоном. 

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

Почему мы проверяем не через базу? Потому что тесты пишут много людей, а подключение к базе — это всегда какая-то одна учётная запись, которую очень удобно спрятать за сервисом, чтобы вызывать программно через клиент или проверять вручную с помощью Swagger. 

Для ручной проверки у каждого нашего сервиса есть Swagger; сюда можно передать sessionId — и командой getEvents достать все события из ClickHouse.


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

Например, для Tracker у нас SLA равно пяти минутам, то есть событие гарантированно долетает до ClickHouse за пять минут. А вот для Google Analytics временной лаг составляет от четырёх часов до суток, поэтому такая проверка не годится. 

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

Проверка событий

Эта проверка есть в каждом тесте в рассмотренных выше примерах:

AnalyticsHelper.expectEventsEquals(etalon, events);

Что скрыто за конструкцией сравнения с эталоном? 

  1. Проверяем, есть ли интересующее нас событие в передаваемом массиве. 
  2. Проверяем значимые поля: в основном ID товаров и пользователей, названия виджетов и категорий. 
  3. Проверяем события на дубли.

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

Тесты трекера мы делим на две группы:

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

Тесты Google Analytics тоже делим на две группы:

  • DataLayer — для проверки действий на странице;
  • перехваты запросов — для проверки переходов между страницами.

Решение проблем

Выше я упоминал, что у нас два основных источника проблем с получением метрик: многочисленные релизы и инциденты эксплуатации. 

Проблемы в релизах мы решаем встраиванием браузерных тестов в пайплайн. 

Пайплайн production-сборки одного из сервисов бэкенда. Пайплайн сайта выглядит примерно так же, только тестов в разы больше.

В production мы используем технологию B/G deploy: после сборки сервисы выкладываются в так называемую синюю зону — пользователи их не видят. Пока сервисы в этой зоне, их можно тестировать. Мы делаем это с помощью браузерных тестов с наивысшим приоритетом. После их успешного прохождения можно пускать на сервисы трафик. 

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

Сверху — график по Tracker, снизу — по Google Analytics. Слева — результаты тестирования аналитики, справа — данные из аналитических систем. Такие панели помогают наблюдать, как отправляются и поступают события. Благодаря этим графикам мы можем в течение дня оповещать вертикальные команды и реагировать на инциденты.

Оценка тестового покрытия по событиям аналитики

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

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

Для разделения событий от пользователей и от тестов у нас встроена механика. Для каждой категории мы ставим свой namespace. Когда по сайту ходят пользователи, аналитические события отправляются с меткой namespace user, а когда тесты — с namespace test. По этим категориям мы строим таблицу со статистикой. 

Приведу несколько курьёзных примеров, которые мы выловили с помощью этой таблицы.

Пользователи кликнули на популярный виджет 5 млн раз — и при этом он не покрыт тестами! Если виджет сломается из-за релиза, это заметит большое количество людей — для нас будут потери.

А вот пример с непопулярным виджетом. На него не идёт трафик, но зато мы его тестируем.

Резюме

В статье рассмотрены три подхода, которые мы в Ozon применяем для тестирования аналитики: 

  1. С использованием DataLayer. 
  2. Перехваты запросов. 
  3. Проверки в конечной системе. 

У каждого подхода есть свои преимущества и недостатки. Поэтому правильное решение — использовать комбинацию подходов.

Мы запускаем автотесты по двум сценариям:

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

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

Обсудить в форуме