Появление unit- и UI-тестов неизбежно в крупных мобильных приложениях: появляется новая функциональность, старая расширяется, изменяются существующие элементы. Для стабильной работы команда вынуждена внедрять автотесты, что требует инфраструктуры, инструментов, а также времени и внимания для их реализации.

Размышляя над этим, пришла идея использовать LLM для автоматической генерации тестов. Это могло бы снизить нагрузку на разработчиков и минимизировать влияние автотестов на time to market новых фич. Меня зовут Марк, я iOS-разработчик Lamoda Tech. Ранее я рассказывал о своем опыте поиска решений с использованием GPT и Copilot в UI-тестах.

Со временем на AI-арене появились новые заметные игроки — DeepSeek и Qwen.  В это же время передо мной как разработчиком возникла задача обеспечить покрытие новой функциональности unit-тестами. Похоже, настал момент для очередного эксперимента: мы протестируем возможности GPT, DeepSeek и Qwen в генерации unit-тестов для iOS-приложения.

Оглавление:

  1. Дамоклов меч AI: фантомные моки в unit-тестах

  2. Генерация unit-тестов с GPT

  3. DeepSeek и Qwen: подтвердят ли они наш опыт с GPT?

  4. GPT, DeepSeek и Qwen: стабильность, точность, выбор

  5. Заключение

Дамоклов меч AI: фантомные моки в unit-тестах

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

Рис.1. *Слева — окно общения с LLM, справа — окно с генерируемым кодом — одно из новых удобных нововведений в GPT
Рис.1. *Слева — окно общения с LLM, справа — окно с генерируемым кодом — одно из новых удобных нововведений в GPT

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

Рис.2
Рис.2

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. Однако такой мок в реальности отсутствует, и результатом будет: 

Рис.3
Рис.3

Xcode безжалостно пресекает подобные предположения чётким и логичным сообщением: “Cannot find type (вставьте нужное) in scope”.

За нами как за разработчиками по-прежнему остаётся настройка базового флоу тестирования: добавление тестового таргета в проект, настройка конфигурации тестов, организация тестов (выбор инструментария, определение хелперов и тулзов, способ подтягивания моков при прогоне тестов и прочее), а также организация тестов в отдельных SPM-модулях, если таковые имеются, и многое другое. И только когда всё это будет готово, и мы получим гарантию существования корректных моков для тестов, сможем обогатить LLM всем необходимым контекстом и попросить его сгенерировать unit-тесты, которые действительно будут работать.

Оптимальный флоу, который я выработал для себя в платной версии GPT — использование готовых пресетов во вкладке «Проекты»:

Рис.4
Рис.4

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

Рис.5
Рис.5
  1. Определяем название проекта.

  2. Загружаем файлы с примерами тестов, используемых в проекте. Это ключевой момент, поскольку в этих файлах можно наглядно продемонстрировать, как покрываются тестами модели, вьюмодели, хелперы и другие компоненты. Важно оставить только код, относящийся к тестам, поскольку импорт пользовательских библиотек и модулей может запутать GPT, что нам ни к чему.

  3. Определяем базовый контекст/запрос, который будет использоваться для каждого нового теста. В моём случае это выглядит так:

«В проекте я использую библиотеки Quick и Nimble для написания unit-тестов. Ориентируйся на файлы ExampleListTests и ExampleTests— они покажут, как именно должны выглядеть тесты. Также есть файл ExampleMock, в котором приведён пример возможной реализации мока для тестов. Я буду передавать код и указывать, какой метод нужно покрыть тестами. Внутри теста используй маркеры: // given, // when, // then». 

Теперь, когда мы самостоятельно создали моки и настроили механизмы их использования, а также подготовили корректный контекст для будущих тестов, можно переходить к генерации unit-тестов с использованием LLM.

Генерация unit-тестов с GPT

Мы собираемся написать unit-тесты для модели “CombinedPaymentMethods”. Для этого создадим метод makeCombinedPaymentMethods, который будет возвращать подготовленный мок-объект.

Рис.6
Рис.6

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

Подготовив мок и отправив код модели, мы получим следующий ответ:

Рис.7
Рис.7

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

Тем не менее это уже шаг вперед. Теперь мы можем встроить в полученную «болванку» собственную реализацию мока и получить следующий результат:

Рис. 8
Рис. 8

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

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

Рис. 9
Рис. 9

На этот раз результат получился вполне удачным. Если моки не нужны, а тестируемый объект не отличается сложностью, GPT может успешно сгенерировать тесты. LLM проанализировала возможные состояния, которые предоставляет хэлпер, передала их в соответствующие методы и проверила различные итоговые результаты.

Таким образом, можно сделать промежуточный вывод: GPT способна создать шаблон тестов, который мы затем доработаем, добавляя важные для нас детали. А если нужно протестировать несложную функциональность, то модель может сразу подготовить базовые сценарии проверки.

DeepSeek и Qwen: подтвердят ли они наш опыт с GPT?

У нас есть два кейса с тестами, которые мы использовали в GPT. Теперь подготовим аналогичные промты для DeepSeek и Qwen и сравним результаты.

На текущий момент ни DeepSeek, ни Qwen не предлагают платных версий, поэтому мы будем использовать только бесплатную функциональность. Одно из главных отличий этих AI от GPT — невозможность повторного использования базового контекста. Это означает, что в каждом новом чате придется заново описывать исходные данные и требования. Построим шаблон стартового промта следующим образом:

«Привет! Мне нужно написать unit-тесты на Swift для iOS-проекта. В нем используются библиотеки Quick и Nimble. Верни код с тестами и кратким объяснением их работы. В тестах используй маркеры: // given // when // then».

Результат DeepSeek:

Рис. 10
Рис. 10
Рис. 11
Рис. 11

Результат Qwen:

Рис. 12
Рис. 12
Рис. 13
Рис. 13

Анализ ответов 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:

Рис. 14
Рис. 14

Результат Qwen:

Рис. 15
Рис. 15

Смотря на полученные результаты, можем выделить следующие аспекты: обе нейросети учитывают заданный контекст, используют указанные библиотеки и стилистически оформляют тесты в соответствии с нашими требованиями. Они проводят базовую проверку функциональности и подготавливают набор тест-кейсов для проверки работы методов и основных состояний хэлпера. При недостатке контекста иногда допускаются вольности в предположениях, но в целом, если нейросетям не требуется работать с неизвестным контекстом моков, результат получается более стабильным.

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-тестов? Поделитесь своим опытом в комментариях.

Комментарии (1)


  1. greed-314
    12.05.2025 08:59

    К сожалению LLM для unit-тестов не использовал, но спасибо большое за статью, очень интересно было прочесть!