Бульварное чтиво из 15+1 мемов.
Мы, разработчики iOS-приложения Додо Пиццы, любим тесты: они дают нам больше уверенности, что ничего не отломалось во время рефакторинга или что только что внедрённая новая фича работает корректно.
Среди всех тестов, которыми мы пользуемся, есть и скриншот-тесты. Для них используем библиотеку swift-snapshot-testing от point free. Она проверяет, что вёрстка на экранах случайно никуда не поплыла.
Скриншот-тесты работают просто:
Пишем тестовый сьют, который проверяет экран или вьюху.
Этот тестовый сьют делает скриншот нашей вьюхи и кладёт его в репозиторий. Такой скриншот становится «эталонным». Expected result в терминах тестирования.
Следующий прогон этого теста снова скриншотит нашу вьюху. Но потом он видит уже существующий эталон в репозитории и понимает, что сейчас надо сравнивать.
Если два скриншота оказываются идентичными, то тест считается пройденным. Если есть различия — тест заваливается.
Звучит хорошо, но реальная жизнь даёт по зубам: даже если никак не менять нашу вьюху, скриншоты могут разойтись. Дело в том, что маки на Intel и маки на Apple Silicon по-разному рендерят тени, скругления и прозрачности.
Мы в команде довольно рано начали закупать маки на Apple Silicon и сразу же столкнулись с этой проблемой.
Решать проблему как-то надо было, и мы нашли одно возможное решение прямо в используемой нами библиотеке — точность сравнения. При тестировании сказали, что скриншотам можно совпасть лишь на 99%.
Часть проблем это решило, но не все. Понижать точность ещё сильнее не хотелось, потому что низкая точность могла пропустить реально важную разницу в скриншотах.
Чтобы решить проблемы до конца, сделали форк библиотеки и докрутили логику сравнения, как нам надо.
Спустя какое-то время библиотека научилась сама это разруливать и мы оказались перед выбором: оставаться на форке или переезжать на официальную версию библиотеки. Вроде не горит, но там то один апдейт, то другой, и перформанс подкрутили ещё. А в нашем форке при этом есть код сравнения картинок, который может понять только один разработчик из команды — это потенциальный блокер.
В общем, решили переезжать. Но когда-нибудь потом, не горит же.
Это история о переезде.
9 января
Первый рабочий день в новом году, ничего сложного делать совсем не хочется. Решаю, что вот он, идеальный момент для переезда с форка. Там же только ссылку в пакетном менеджере обновить, делов-то.
Создаю ветку, обновляю и тут же понимаю, что этого недостаточно: надо обновить ссылку ещё и в DBUIKit
— нашем UI-модуле, который к Додо Пицце и ещё к нескольким приложениям подключается.
10 января
Обновляю ссылку в DBUIKit
, подключаю правильный комит к приложению Додо Пиццы и запускаю обновление зависимостей. Не обновляются.
Оказывается, библиотека снапшот-тестов перестала поддерживать Carthage. А мы именно так её и подключали.
11 января
Перевожу библиотеку с Carthage на SPM. Запускаю обновление зависимостей. Не обновляются.
Ещё бы: теперь библиотека снапшот-тестов тянется явно через SPM и неявно через Carthage вместе с подключённым через него DBUIKit
. Значит, надо и DBUIKit
на SPM перевести.
12 января
Перевожу DBUIKit на SPM, запускаю обновление зависимостей. Не обновляются.
Оказывается, библиотека DBUIKit
неявно тянет через SPM за собой один общий модуль. И этот же самый модуль тянется в наше приложение явно через Carthage
. Хорошо, что этот модуль — легаси, и его уже давно руки чесались выпилить. Выпиливаю. Запускаю обновление зависимостей. Не обновляются.
Не углядел — оказывается, есть ещё пачка общих модулей, которые за собой тянет DBUIKit через SPM и которые явно ставятся через Carthage. Они ещё не легаси, так что, получается, надо их переводить на SPM. Мы даже хотели это сделать когда-то, просто сейчас чуть пораньше придётся этим заняться.
13 января
Откладываю ветку, в которой с форка переезжаю. Вместо неё создаю ветку, в которой переезжаю с Carthage на SPM. Какой русский не любит переезжать в 2023.
Перевожу всё за 10 минут, запускаю обновление зависимостей. Обновляются.
Комичу, пушу, создаю пулл-реквест. CI запускает тесты, и они спустя час падают по таймауту. Даже артефакты не сохранились.
Запускаю тесты локально и вижу картину: Spry, библиотека для моков, которую мы используем, стала крашить при сравнении объектов. Не тест заваливать, а именно крашить. И при этом вместе с ней крашится и симулятор. Xcode в таком случае перезапускает симулятор и продолжает гонять тесты. Симулятор снова крашит. Xcode снова перезапускает. Симулятор снова крашит.
Крашить стали вообще все тесты, в которых эта библиотека сравнивает ожидаемые аргументы функций с актуальными. Тестов таких много. Перезапуск симулятора занимает сколько-то секунд. Вот это вот всё и приводит к тому, что тесты на CI не укладываются в час и падают по таймауту.
Пытаемся с ребятами понять, почему Spry стал крашить, но не понимаем.
16 января
В команде Spry не всем нравился. Решили, что раз он стал блокером, то пора его выпиливать. Делаю поиск по проекту, пытаюсь оценить объём работ, охаю. Начинаю придумывать, какие углы можно срезать. Вспоминаю, что крашат только те тесты, где аргументы сравниваются и понимаю, что достаточно переписать только такие.
17 января
Начинаю переписывать.
4 февраля
Заканчиваю переписывать.
27 пулл-реквестов.
111 комитов.
3445 добавленных строк кода.
5967 удалённых строк кода.
22 выпитых кофе.
6 февраля
Возвращаюсь к ветке, в которой переезжаю с форка на оригинальную репу библиотеки снапшот-тестов. Актуализирую до последних изменений в проекте, то есть подливаю выпиленные тесты, которые крашили симулятор. Собираю проект, прогоняю тесты — падают. Ожидаемо — закастомайзенный механизм сравнения скриншотов заменился на другой.
7 февраля
Начинаю обновлять тесты, чтобы они использовали новое API для указания точности сравнения.
8 февраля
Задаюсь вопросом, а почему вообще скриншот-тесты на CI заваливаются — у нас уже и у всех разрабов Apple Silicon, и на CI у нас Apple Silicon, и даже Github Actions Self-Hosted Runner уже Native ARM64. Проверяю гипотезу и убеждаюсь, что даже между M1 и M1 Pro есть разница в рендере.
9 февраля
Заканчиваю обновлять тесты.
10 февраля
Создаю пулл-реквест со своим переездом на оригинальное репо и с обновлёнными тестами.
CI запускает тесты и радостно оповещает, что там билд заваливается.
Нахожу проблему: отсутствует явная линковка между некоторыми нашими модулями, из-за чего сборка может флаковать.
Исправляю линковку в конфиге проекта, комичу, пушу. CI снова запускает тесты. Теперь всё собралось и всё прошло.
Вливаю.
В очередной раз убедился, что простых задач не бывает.
Морали не будет, мемы закончились, спасибо всем, кто дочитал.