Автоматизация тестирования продуктовой аналитики в мобильных приложениях
Тестирование всех событий продуктовой аналитики перед каждым релизом обычно отнимает время. Но это можно автоматизировать. Даниил Субботин, железный разработчик Redmadrobot, на примере iOS-приложения показывает, как это сделать.
Вы когда-нибудь выпускали релиз, в котором случайно удалили код отправляющий некоторые важные события аналитики? Или забывали покрыть событиями новую фичу? А сколько времени ваши аналитики или тестировщики тратят на ручное тестирование перед каждым релизом? А если это приложение с тысячей событий?
В этой статье расскажу, как автоматизировать тестирование продуктовой аналитики, чтобы избежать проблем и сэкономить время и деньги.
Тестирование аналитики вручную
Когда пользователь совершает действие, то событие сразу отправляется в систему аналитики. И, к сожалению, это никак не отследить, чтобы протестировать. Только если модифицировать код приложения, чтобы вместе с отправкой события сделать дополнительное действие. Какие есть варианты:
- Можно отправить локальное уведомление (типа Push) с названием и параметрами события. Это неудобно, так как перекрывает интерфейс приложения, а также сложно тестировать цепочку событий из-за того, что каждое новое уведомление перекрывает старые.
- Добавить отладочный экран, на котором показан список всех отправленных событий. Но это тоже не очень удобно — нужно постоянно переключаться между приложением и этим экраном.
- Либо события аналитики можно логировать и сразу отслеживать в консоли.
Третий вариант — самый удобный: его несложно сделать и он позволяет фильтровать события. Также при тестировании приложения сразу видно, какие события отправляются. И если приложение содержит небольшое количество событий, то этим вариантом можно обойтись, не прибегая к автоматизации.
Это всё способы тестирования события аналитики вручную. Но если в приложении событий много, то такое тестирование будет не быстрым.
Чтобы автоматизировать его, можно воспользоваться UI-тестами. С их помощью можно переходить между экранами, совершать действия и проверять, что определенные события с указанными параметрами отправляются.
Тестирование аналитики UI-тестами
У любого события есть имя, у некоторых бывают еще и параметры. Например, у «успешность авторизации» имя authorization и булевый параметр success.
Вообще, из UI-тестов нельзя узнать, какие события отправило приложение. Когда пользователь совершает действие, то они сразу попадают в систему аналитики. Но в этот момент их можно перехватить и сохранить в место, куда у UI-тестов есть доступ.
На практике существуют два способа передачи данных из приложения в UI-тесты:
- Можно сохранить текстовые данные в невидимое текстовое поле или в свойство accessibilityLabel невидимой «вьюшки». Но в этом случае меняется иерархия «вьюшек», и это может привести к багам. Кроме того, не получится очистить список отправленных событий из UI-тестов.
- Или можно сохранить текстовые данные в буфер обмена, к которому у UI-тестов есть доступ. Этот вариант лучше, так как иерархия «вьюшек» не изменяется. Буфер обмена можно очистить из UI-тестов, а еще это проще в реализации.
Когда приложение запущено в режиме UI-тестирования, то можно подменить сервис отправки событий аналитики. Например, вместо AppMetrica подставить свой сервис, который будет отправлять события в буфер обмена. Далее в UI-тестах происходит чтение текстовых данных из буфера, преобразование их в массив событий и проверка.
Так в итоге будет выглядеть UI-тест, проверяющий события аналитики на экране авторизации:
Доработки со стороны приложения
Расскажу о том, как доработать код приложения, чтобы события аналитики отправлялись и в систему аналитики, и в буфер обмена в зависимости от переданных аргументов при запуске приложения.
Базовые сущности
Представим событие аналитики в виде следующей структуры:
Структура MetricEvent будет использоваться и в коде приложения, и в коде UI-тестов. Поэтому вынесем её в отдельный модуль — MetricExampleCore. Для этого нужно создать новый Target типа Framework.
Событие что-то должно отправлять, поэтому объявим соответствующий протокол:
В первой строчке импортируем модуль, в котором объявили структуру MetricEvent.
Сервисы отправки событий
Этому протоколу будут соответствовать классы, отправляющие события куда-либо. К примеру, класс для отправки событий в AppMetrica:
В нашем случае нужен класс, который отправляет события в буфер обмена. Создаем его:
В методе send можно проверить, что приложение запущено в режиме UI-тестирования и разрешена отправка событий в буфер обмена. Затем в массив всех отправленных событий добавляется новое.
После этого массив представляется в виде текста с использованием метода encodeMetricEvents. Там каждое событие преобразуется в словарь и полученный массив сериализуется. После этого строка сохраняется в буфер обмена.
Каждый UIViewController, который будет отправлять события, получит в инициализатор зависимость MetricService.
Чтобы не передавать каждый раз вручную эту зависимость, можно использовать паттерн Service Locator и создать класс ServiceLayer. В нем будет создаваться и храниться MetricService, который будет передаваться во все контроллеры.
Если приложение запущено в режиме UI-тестирования, то для отправки событий используется MetricServiceForUITests. В ином случае AppMetricaService.
Отправка событий
Осталось объявить все события, которые будут отправляться. Для этого нужно написать расширение MetricEvent:
Теперь события можно отправлять:
Аргументы запуска
Я уже упоминал такие вещи, как:
При запуске UI-тестов на аналитику будут передаваться два аргумента: --UI-TESTING и --SEND-METRICS-TO-PASTEBOARD.
Первый показывает, что приложение запущено в режиме UI-тестирования. Второй — что приложению разрешено отправлять события аналитики в буфер обмена. Чтобы получить доступ к этим аргументам, нужно написать расширение для ProcessInfo:
Доработки со стороны UI-тестов
Теперь расскажу, как на стороне UI-тестов получить список отправленных событий из буфера обмена и проверить их.
Получение списка отправленных событий
Чтобы получить текстовые данные из буфера, используем UIPasteboard.general.string. Затем строку нужно преобразовать в массив событий (MetricEvent). В методе decodeMetricEvents строка преобразуется в объект Data и десериализуется в массив с помощью JSONSerialization:
Далее массив словарей преобразуется в массив MetricEvent. Для этого у MetricEvent нужно добавить инициализатор из словаря:
Теперь можно получить массив событий [MetricEvent] и проанализировать его.
Если в процессе тестирования понадобится очистить список событий, то тут поможет:
Проверки списка событий
Можно написать несколько вспомогательных методов, которые будут проверять массив событий. Вот один из них: он проверяет наличие события с указанным именем.
В итоге получился класс AnalyticsTestBase. Посмотреть его можно на GitHub — AnalyticsTestBase.swift
Создадим класс, наследника XCTestCase, от которого будут наследоваться классы, тестирующие аналитику. Он создает класс AnalyticsTestBase для тестирования аналитики и метод launchApp, запускающий приложение.
Метод будет принимать AppLaunchParameters (параметры запуска приложения, о которых я говорил выше).
В обычных UI-тестах приложение будет запускаться с параметрами:
А в UI-тестах на аналитику:
Теперь можно писать тесты на аналитику. Например, это тест на экран входа:
LoginScreen — это Page Object, описывающий экран авторизации. Посмотреть его можно на GitHub — LoginScreen.swift
Примеры
Example проект
Это простое приложение, состоящее из двух экранов: вход и меню. События отправляются при заходе на каждый экран, при авторизации и при выборе пункта меню.
Тест, покрывающий все эти события:
Реальный проект
Пример UI-тестов на аналитику экрана авторизации из реального проекта — LoginAnalyticsTests.swift
Пример, как мне, разработчику, помогли UI-тесты на аналитику. На одном проекте нужно было произвести рефакторинг и редизайн главного экрана приложения. Экран был сложным, с большим количеством событий аналитики.
На тот момент в проекте я уже настроил тесты. После рефакторинга и редизайна запустил тесты и обнаружил, что некоторые события случайно удалил. Если бы не тесты на аналитику, эти события не попали бы в релиз.
Итоги
Плюсы подхода:
- Продуктовому аналитику или тестировщику не нужно проверять все события аналитики вручную. А это экономия времени и, соответственно, денег.
- Если у вас настроен CI, то UI-тесты на аналитику можно запускать по расписанию, например, раз в неделю или по команде из Slack.
Есть и минусы:
- UI-тесты выполняются относительно долго. Имеет смысл запускать их только в процессе регрессионного тестирования перед каждым релизом.
- UI-тесты на аналитику смогут написать только те тестировщики, которые имеют опыт написания нативных UI-тестов.
В случае ручного тестирования, при добавлении новых событий, нужно линейно больше времени на тестирование. Автоматизированное тестирования быстрее, но для него нужно подготовить инфраструктуру: это займет некоторое время. Но после этого добавление теста на новое событие будет проходить быстрее.
Поэтому в случае большого проекта есть смысл автоматизировать проверку событий аналитики.