Появление 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-тестов? Поделитесь своим опытом в комментариях.
Комментарии (4)
 - Bardakan12.05.2025 08:59- Я вижу, что вы проверили, генерирует ли нейросеть тесты вообще, чтобы они компилировались. Видимо, да. - А теперь вопросы, на которые ответы не увидел. - 1)какое покрытие тестами в итоге получилось? Я мало работал с тестированием, но судя по последней картинке там с этим все плохо. Вместо того, чтобы проверить иконку каждого пункта enum, библиотека на рандоме выбрала пару кейсов и проверила их, опустив остальное - 2)какой процент бесполезных тестов? Это те, написание которых ничего не меняет. Для оценки этого в статье не хватает кода  - techTalesmith Автор12.05.2025 08:59- 1) В конечном итоге я самостоятельно покрыл весь требуемый функционал тестами. Да, LLM могут не покрывать фичу полностью, по-этому чаще всего приходится доводить до конца покрытие самостоятельно. 
 2) Точный процент сказать сложно, с рекомендациями от LLM по тестам стоит быть крайне аккуратным. Лучшая связка связка которую я смог найти, это генерировать "карказ теста", и прописывать сами тесты самосстоятельно, добавляя необходимые зависимости в ручную
 
 
           
 
greed-314
К сожалению LLM для unit-тестов не использовал, но спасибо большое за статью, очень интересно было прочесть!
techTalesmith Автор
Супер! Большое спасибо!