Кнопка ломалась в каждом релизе iOS-приложения: мы закрыли баг автотестами
Регрессионное UI-тестирование для iOS — это набор автоматических сценариев на XCUITest, которые проходят через реальный интерфейс приложения при каждой сборке и ловят сломанные экраны до релиза. Mock backend подменяет боевой API заглушкой с предсказуемыми ответами, а XcodeBuildMCP управляет сборкой и запуском тестов через единый протокол вместо ручных команд xcodebuild.
кнопка «отправить заказ» ломалась в каждом третьем релизе
Клиент — оптовая компания с iOS-приложением для торговых представителей, которое отправляет заказ прямо в 1С: Управление торговлей. Экран простой: корзина, кнопка «отправить», статус в ответ. Но именно этот экран после почти каждого крупного релиза переставал работать — то кнопка не активировалась при пустом остатке на складе, то приложение зависало, если 1С отвечала дольше пары секунд, то терялась сумма заказа при возврате из фона на экран заказа. QA-инженер проверял этот сценарий вручную перед каждым TestFlight-билдом, но за час-полтора ручного прогона он покрывал десяток типовых кейсов, а регрессия каждый раз пряталась в одиннадцатом — там, где сходятся сразу два условия: слабый остаток на складе и медленный ответ сервера.
Разработчик менял код в соседнем модуле — авторизации или push-уведомлений, — и заказ снова ломался, потому что зависимость была неочевидной: общий сетевой слой, общий кеш токена, общая модель корзины. Поэтому релизы стали выходить по вторникам поздно вечером, чтобы к утру среды успеть откатить билд, если торговые представители в поле пожалуются, что заказ «завис».
цена одного пропущенного регресса
Для B2B-приложения сломанная отправка заказа — это не косметический баг, а остановленная продажа. Менеджер стоит у клиента на складе, пытается отправить заказ с планшета, приложение не отвечает — и он звонит в отдел продаж, диктует позиции голосом, а оператор вбивает их в 1С руками, теряя точность и время. Тот же вечер уходит на разбор инцидента: искать, в каком билде появилась регрессия, кто из разработчиков её внёс, откатывать релиз в App Store Connect — а откат там не мгновенный, предыдущая версия ещё сутки висит на модерации, прежде чем попадёт в стор. Пока идёт откат, часть менеджеров перестаёт доверять приложению и возвращается к звонкам и Excel, а вернуть их обратно после инцидента получается не сразу: одного удачного релиза недостаточно, нужно несколько подряд без сюрпризов.
три варианта тестирования, которые мы сравнили
Прежде чем писать первый тест, мы прикинули, на чём собирать регрессионный набор для iOS. Разбирали три подхода — от нативного XCUITest до визуального сравнения скриншотов.
| подход | что проверяет | нужен ли mock backend | кто поддерживает после релиза |
|---|---|---|---|
| XCUITest | реальные нажатия по интерфейсу, доступность элементов, переходы между экранами и сетевые вызовы | да, иначе тест зависит от состояния боевой 1С | iOS-разработчик, тесты живут в том же Xcode-проекте |
| Appium | то же самое, но через кросс-платформенный драйвер поверх XCUITest | да | отдельный QA-automation инженер со знанием Appium и WebDriver |
| snapshot-тестирование | сравнение скриншотов экрана с эталонным изображением | частично — данные на экране всё равно нужно зафиксировать | разработчик, но эталоны «плывут» при любой правке верстки |
Appium добавляет отдельный сервер и драйвер поверх того же XCUITest, чтобы один и тот же тест теоретически можно было запускать и на iOS, и на Android. Для приложения, у которого Android-версии нет и не планируется, этот слой не окупается — лишняя точка отказа и лишний процесс, который нужно поднимать перед каждым прогоном. Snapshot-тестирование ловит визуальные отличия — сдвинутую кнопку, обрезанный текст, — но не ловит логическую ошибку вроде «кнопка нажимается, а запрос в 1С уходит с пустым телом»: скриншот в этот момент выглядит абсолютно нормально. Мы выбрали чистый XCUITest: он нативный, встроен в Xcode, не требует внешних зависимостей и обновляется вместе с SDK при каждом апдейте iOS, а не догоняет его через месяц-два, как сторонние драйверы.
mock backend вместо живой 1С в тестах
Первая попытка запускать XCUITest прямо против тестового контура 1С провалилась в первый же день: контур был общий с ручными тестировщиками, кто-то менял остатки на складе прямо во время прогона автотестов, и сценарий «заказ отправлен успешно» через раз падал не из-за бага в приложении, а из-за чужих данных на общей базе. Тесты, которые падают без видимой причины, быстро перестают вызывать доверие — их начинают перезапускать не глядя, «может, само пройдёт», и тогда смысл в них теряется.
Мы вынесли перед приложением локальный mock-сервер: он отдаёт фиксированные JSON-ответы на каждый запрос к API интеграции с 1С — успешную отправку заказа, отказ по нехватке остатка, таймаут дольше пяти секунд, разрыв соединения посреди запроса. Каждый тестовый сценарий явно указывает, какой ответ должен вернуть mock на этот раз, поэтому тест воспроизводим: тот же билд на том же наборе фикстур всегда даёт тот же результат, независимо от того, что происходит в боевой базе клиента в этот момент. Отдельно завели фикстуру с «медленным» ответом в пять-семь секунд — именно на ней ловится тот самый баг с зависанием при слабом отклике 1С, который на быстром тестовом контуре ни разу не воспроизводился.
как XcodeBuildMCP встал в конвейер сборки
Сборка и прогон XCUITest через голый xcodebuild из командной строки — это километры лога, где нужный результат теста приходится выгребать grep-ом по ключевым словам, а любое изменение формата вывода Xcode после очередного обновления ломает самодельный скрипт сборки. Мы поставили перед xcodebuild и simctl прослойку XcodeBuildMCP — она превращает сборку проекта, установку на симулятор, запуск конкретного тестового таргета и снятие лога в набор отдельных команд со структурированным, машиночитаемым результатом вместо текстового лога вперемешку с предупреждениями компилятора и сотнями строк линковщика.
На практике это избавило CI-скрипт от разбора случайных строк вида «Test Suite passed» посреди тысяч строк постороннего вывода: сборка, выбор нужного симулятора, установка билда и запуск конкретного набора тестов стали отдельными шагами с понятным результатом на каждом — собралось или нет, прошёл тест или упал, и на каком именно шаге. Если тест падает, лог падения приходит в привязке к конкретному шагу конвейера, а не общей простынёй, где нужную строку приходится искать глазами.
что именно проверяют тесты
Тесты в этом наборе гоняют один и тот же путь пользователя через реальный интерфейс — не вызывают внутренние методы напрямую, а нажимают на те же кнопки, что и живой человек. На каждый прогон приходится несколько вариантов одного и того же экрана заказа: успешная отправка с обычным остатком на складе, попытка отправить заказ при нулевом остатке по одной из позиций, разрыв соединения в момент отправки, медленный ответ от 1С длиной пять-семь секунд, и возврат в приложение из фонового режима с уже заполненной, но не отправленной корзиной. Пять сценариев на один экран звучат немного, но именно они закрывают тот самый одиннадцатый случай, который ускользал от ручной проверки: сочетание условий, а не единичная ошибка.
Отдельно тесты проверяют, что приложение не теряет данные при обрыве сети посередине запроса — это тот случай, который вручную воспроизвести сложно: нужно физически выключить wi-fi в нужный момент, а mock-сервер обрывает соединение по команде в самом тесте, без ручных манипуляций с сетью.
что изменилось после внедрения
Регрессия в отправке заказа теперь ловится на этапе сборки, до того как билд вообще попадает в TestFlight, — тест на этот сценарий гоняется при каждом пул-реквесте, а не раз в неделю руками перед релизом. QA-инженер перестал тратить время на повторную проверку одних и тех же десяти-пятнадцати экранов перед каждым релизом и переключился на исследовательское тестирование новых фич, где ручная проверка действительно нужна и где автотест ещё не написан. Релизы вернулись на обычный рабочий график — без вечерних авралов по вторникам и без ночных откатов в App Store Connect.
Разработчики тоже стали спокойнее менять общий код: если правка в модуле авторизации задевает отправку заказа, тест падает на той же сборке, где сделана правка, а не через две недели на проде у клиента.
сколько стоит такая система тестирования
Регрессионный набор на XCUITest с mock backend — это не разовый скрипт, а часть инфраструктуры проекта: его нужно поддерживать вместе с приложением, добавлять сценарии под новые экраны и обновлять mock-ответы при каждом изменении контракта с 1С. Для новых B2B-приложений с 1С мы закладываем такой набор тестов сразу в архитектуру проекта, а не пристраиваем его после первых регрессий в проде — это дешевле и по деньгам, и по нервам команды.
Стоимость разработки мобильного приложения с интеграцией и тестовой инфраструктурой начинается от 500 000 ₽ — итоговая цена зависит от числа экранов, сложности интеграции с 1С и объёма регрессионного покрытия, актуальные условия — на странице цены и тарифы. Если у вас уже есть приложение без автотестов и релизы регулярно откатываются — можно подключить XCUITest и mock backend к существующему проекту, не переписывая его с нуля; посмотреть, как мы ведём такие проекты, можно в разделе разработка мобильных приложений.
❓ Частые вопросы
Зачем нужен mock backend, если можно тестировать на реальной 1С?
Реальная 1С отвечает по-разному в зависимости от текущих данных и нагрузки, поэтому тест то падает, то проходит без изменений в коде. Mock-сервер отдаёт фиксированный ответ на каждый сценарий, и тест всегда воспроизводим: один и тот же билд на одних и тех же данных даёт один и тот же результат.
Сколько времени занимает добавление таких тестов к готовому приложению?
Зависит от числа ключевых сценариев в приложении. Mock backend и первый набор тестов на самые критичные экраны — отправку заказа, авторизацию, оплату — обычно встают в проект за несколько недель параллельно с текущей разработкой, без остановки текущих релизов и без заморозки фич.
XCUITest подходит только для приложений с интеграцией 1С?
Нет, это универсальный инструмент Apple для тестирования любого iOS-приложения через интерфейс. Мы используем связку с mock backend именно там, где приложение зависит от внешнего API — 1С, платёжного шлюза, CRM, — чтобы тесты не зависели от состояния и доступности боевой системы.
Что делать, если автотесты начинают падать без видимой причины?
Такие тесты называют flaky — обычно причина в реальном сетевом запросе или в анимации интерфейса, которую тест не дождался. Решение то же, что и с 1С: убрать зависимость от живого API через mock-сервер и добавить явное ожидание нужного состояния экрана вместо фиксированной паузы.
Можно ли встроить такие тесты в уже существующий CI без переписывания проекта?
Да. XcodeBuildMCP и mock-сервер добавляются поверх текущей структуры проекта, без переписывания бизнес-логики самого приложения. Меняется только конвейер сборки: появляются отдельные шаги сборки, установки на симулятор и прогона тестов с понятным результатом на каждом из них.
Или позвоните: +7 906 045-28-27 — в рабочее время с 9:00 до 19:00
Читайте также
Остались вопросы? Нужна помощь?
Менеджеры компании с радостью ответят на ваши вопросы, произведут расчет стоимости услуг и подготовят индивидуальное коммерческое предложение.
Бесплатная консультация

