Появление unit- и UI-тестов неизбежно в крупных мобильных приложениях: появляется новая функциональность, старая расширяется, изменяются существующие элементы. Для стабильной работы команда вынуждена внедрять автотесты, что требует инфраструктуры, инструментов, а также времени и внимания для их реализации.
Размышляя над этим, пришла идея использовать LLM для автоматической генерации тестов. Это могло бы снизить нагрузку на разработчиков и минимизировать влияние автотестов на time to market новых фич. Меня зовут Марк, я iOS-разработчик Lamoda Tech. Ранее я рассказывал о своем опыте поиска решений с использованием GPT и Copilot в UI-тестах.
Со временем на AI-арене появились новые заметные игроки — DeepSeek и Qwen. В это же время передо мной как разработчиком возникла задача обеспечить покрытие новой функциональности unit-тестами. Похоже, настал момент для очередного эксперимента: мы протестируем возможности GPT, DeepSeek и Qwen в генерации unit-тестов для iOS-приложения.

Оглавление:
Дамоклов меч AI: фантомные моки в unit-тестах
Сразу перейдём к делу. Я использую платную версию GPT. Возьмём простую структуру и попросим модель сгенерировать unit-тесты:

Выглядит неплохо, но думаю, вы уже догадались, в чём подвох. Если нет, давайте познакомим этот код с Xcode:

Xcode безжалостно подсвечивает ошибки, и на этом примере можно увидеть ключевую проблему генерации unit-тестов в LLM — модель пытается предполагать контекст, который ей недоступен. Если вернуться к промту в начале, видно, что LLM обладает информацией о struct PaymentPromo, но не знает, что содержит struct Promo, используемый в PaymentPromo. Однако это не мешает GPT предположить, что Promo включает поля id, name и discount. Так как этих полей в реальности нет, возникает ошибка: “Extra arguments at positions #1, #2, #3 in call”.
GPT также склонен упрощать себе задачу весьма любопытным способом. Если в промте представлено большое количество кода, модель может сделать вывод, что у нас уже есть готовый мок, например, PromoBannerSallerMock
. Однако такой мок в реальности отсутствует, и результатом будет:

Xcode безжалостно пресекает подобные предположения чётким и логичным сообщением: “Cannot find type (вставьте нужное) in scope”.
За нами как за разработчиками по-прежнему остаётся настройка базового флоу тестирования: добавление тестового таргета в проект, настройка конфигурации тестов, организация тестов (выбор инструментария, определение хелперов и тулзов, способ подтягивания моков при прогоне тестов и прочее), а также организация тестов в отдельных SPM-модулях, если таковые имеются, и многое другое. И только когда всё это будет готово, и мы получим гарантию существования корректных моков для тестов, сможем обогатить LLM всем необходимым контекстом и попросить его сгенерировать unit-тесты, которые действительно будут работать.
Оптимальный флоу, который я выработал для себя в платной версии GPT — использование готовых пресетов во вкладке «Проекты»:

Здесь у нас есть весь необходимый минимальный инструментарий, позволяющий задать правильный базовый контекст для всей предстоящей работы:

Определяем название проекта.
Загружаем файлы с примерами тестов, используемых в проекте. Это ключевой момент, поскольку в этих файлах можно наглядно продемонстрировать, как покрываются тестами модели, вьюмодели, хелперы и другие компоненты. Важно оставить только код, относящийся к тестам, поскольку импорт пользовательских библиотек и модулей может запутать GPT, что нам ни к чему.
Определяем базовый контекст/запрос, который будет использоваться для каждого нового теста. В моём случае это выглядит так:
«В проекте я использую библиотеки Quick и Nimble для написания unit-тестов. Ориентируйся на файлы ExampleListTests и ExampleTests— они покажут, как именно должны выглядеть тесты. Также есть файл ExampleMock, в котором приведён пример возможной реализации мока для тестов. Я буду передавать код и указывать, какой метод нужно покрыть тестами. Внутри теста используй маркеры: // given, // when, // then».
Теперь, когда мы самостоятельно создали моки и настроили механизмы их использования, а также подготовили корректный контекст для будущих тестов, можно переходить к генерации unit-тестов с использованием LLM.
Генерация unit-тестов с GPT
Мы собираемся написать unit-тесты для модели “CombinedPaymentMethods”. Для этого создадим метод makeCombinedPaymentMethods
, который будет возвращать подготовленный мок-объект.

Неважно, как именно мы создаем мок: возвращаем заполненную модель или используем готовый JSON-файл с заранее подготовленными данными. Этот этап содержит много контекста, который недоступен LLM. По сути, только разработчики, участвовавшие в планировании и создании данной модели, могут знать, за что отвечает каждое поле, для чего оно используется и насколько оно важно в работе. Исходя из этого, будет строиться будущий тест. Так как GPT не обладает этим контекстом, она не сможет на него опереться и корректно его использовать.
Подготовив мок и отправив код модели, мы получим следующий ответ:

Результат уже значительно лучше. GPT старается соблюдать единый стиль написания теста и использует маркеры // given, // when, // then для логического разделения кода. Однако по-прежнему игнорирует созданный нами метод makeCombinedPaymentMethods
.
Тем не менее это уже шаг вперед. Теперь мы можем встроить в полученную «болванку» собственную реализацию мока и получить следующий результат:

В результате мы добились желаемого — написали unit-тесты для нашей модели в едином стиле с уже существующими тестами. Конечно, GPT не может сразу выдать идеальный результат, но «болванка» упростила процесс. Вместо того чтобы писать тест с нуля, нам нужно было лишь доработать ключевые моменты, которые имели для нас значение.
Теперь попробуем написать unit-тесты для хэлпера. Здесь моки не понадобятся — достаточно попросить GPT использовать контекст самого хэлпера и уже полученные ранее знания для генерации тестов. LLM подготовит такой вариант покрытия хелпера тестами.

На этот раз результат получился вполне удачным. Если моки не нужны, а тестируемый объект не отличается сложностью, GPT может успешно сгенерировать тесты. LLM проанализировала возможные состояния, которые предоставляет хэлпер, передала их в соответствующие методы и проверила различные итоговые результаты.
Таким образом, можно сделать промежуточный вывод: GPT способна создать шаблон тестов, который мы затем доработаем, добавляя важные для нас детали. А если нужно протестировать несложную функциональность, то модель может сразу подготовить базовые сценарии проверки.
DeepSeek и Qwen: подтвердят ли они наш опыт с GPT?
У нас есть два кейса с тестами, которые мы использовали в GPT. Теперь подготовим аналогичные промты для DeepSeek и Qwen и сравним результаты.
На текущий момент ни DeepSeek, ни Qwen не предлагают платных версий, поэтому мы будем использовать только бесплатную функциональность. Одно из главных отличий этих AI от GPT — невозможность повторного использования базового контекста. Это означает, что в каждом новом чате придется заново описывать исходные данные и требования. Построим шаблон стартового промта следующим образом:
«Привет! Мне нужно написать unit-тесты на Swift для iOS-проекта. В нем используются библиотеки Quick и Nimble. Верни код с тестами и кратким объяснением их работы. В тестах используй маркеры: // given // when // then».
Результат DeepSeek:


Результат Qwen:


Анализ ответов DeepSeek и Qwen позволяет выделить их общие сильные и слабые стороны.
Плюсы моделей:
Правильно идентифицируют и используют метод
makeCombinedPaymentMethods
в моках.Следуют принципу структурирования тестов по // given // when // then (с разной степенью строгости).
Минусы моделей:
Не просто используют
makeCombinedPaymentMethods
, но и самостоятельно модифицируют мок, адаптируя его под свой сценарий теста, когда это не требуется.
DeepSeek, например, в тесте context("when initialized with payment errors")
добавляет paymentErrors: [PaymentMethod.Error]
, а Qwen в тесте context("when initialized with custom initializer")
изменяет параметры methods
, paymentErrors
и другие.
Хотя DeepSeek поддерживает загрузку референсных файлов (в отличие от Qwen), это не дает ему преимущества — в целом подход к генерации тестов у обоих моделей схож.
Главная проблема, которую мы видим, — нейросетям DeepSeek и Qwen (как и GPT) сложно работать с моками. Они не всегда корректно интерпретируют этот контекст и часто предполагают лишние детали.
Давайте посмотрим, на что способны DeepSeek и Qwen в следующем сценарии, когда нам нет необходимости использовать моки и нужно проверить работу несложного хэлпера PaymentsHelper:
Результат DeepSeek:

Результат Qwen:

Смотря на полученные результаты, можем выделить следующие аспекты: обе нейросети учитывают заданный контекст, используют указанные библиотеки и стилистически оформляют тесты в соответствии с нашими требованиями. Они проводят базовую проверку функциональности и подготавливают набор тест-кейсов для проверки работы методов и основных состояний хэлпера. При недостатке контекста иногда допускаются вольности в предположениях, но в целом, если нейросетям не требуется работать с неизвестным контекстом моков, результат получается более стабильным.
GPT, DeepSeek и Qwen: стабильность, точность, выбор
Сравнивая ответы GPT, DeepSeek и Qwen по двум тестовым кейсам, можно отметить несколько общих черт:
Все модели сталкиваются с проблемой «предугадывания» неизвестного контекста, что приводит к созданию некорректных unit-тестов, опирающихся на несуществующие файлы.
Искусственные интеллект-системы генерируют базовые тестовые сценарии, такие как проверки на равенство, значения nil и другие стандартные кейсы.
Хотя нейросети могут создать «болванку» будущего unit-теста, они не способны автоматически сгенерировать полностью верное и рабочее решение.
В целом, их ответы схожи: при одинаковых промтах модели выдают почти идентичные результаты, имея одни и те же сильные и слабые стороны.
Так что же выбрать в конечном итоге?
Если вы только начинаете знакомство с миром unit-тестов на iOS, любая из этих нейросетей подойдет для первого опыта и станет полезным помощником на начальном этапе.
Если вы только начинаете использовать LLM для написания unit-тестов, попробуйте DeepSeek и Qwen. Они бесплатны, и если работа с AI-помощником окажется для вас удобной, со временем можно перейти на платные версии аналогичных сервисов при необходимости.
Если вы уже пользуетесь GPT (платной или бесплатной версией), переход на китайские аналоги вряд ли будет оправдан. В платной версии GPT вы получите более удобный рабочий процесс с возможностью запоминания базовых сценариев для повторяющихся промтов, чего пока нет у DeepSeek и Qwen. Кроме того, как показали тесты, ответы всех этих моделей во многом схожи.
Если вы уже работаете с GPT, стоит ли переходить на ее платную версию? Кардинально новых инсайтов в написании unit-тестов это не принесет, но дополнительные возможности, такие как большее число промтов, загрузка файлов и удобная организация повторяющихся запросов, могут значительно облегчить вашу работу.
Заключение
В конечном итоге приходят следующие мысли:
Крупные модели все больше сходятся в своем понимании и формировании «верного» ответа.
LLM способны сгенерировать «макет» будущего решения, но не итоговую реализацию.
Вам как специалисту предстоит самостоятельно решить, использовать ли LLM в своей работе, и если да — то как найти для нее полезное и эффективное применение.
Как писал Кевин Келли в своей книге The Inevitable: Understanding the 12 Technological Forces That Will Shape Our Future (2016): «Мы и машины развиваемся вместе, и с каждым шагом становится все труднее сказать, кто на кого больше влияет».
Использовали ли вы LLM для написания unit-тестов? Поделитесь своим опытом в комментариях.
greed-314
К сожалению LLM для unit-тестов не использовал, но спасибо большое за статью, очень интересно было прочесть!