Я управляю тестированием ИИ-моделей 4 года. Что я понял за это время?. qa.. qa. qa engineer.. qa. qa engineer. qa lead.. qa. qa engineer. qa lead. qa management.. qa. qa engineer. qa lead. qa management. qa strategy.. qa. qa engineer. qa lead. qa management. qa strategy. qa testing.

Привет!

Меня зовут Валентин, я — руководитель направления тестирования моделей машинного обучения в Альфа-Банке. Моя команда занимается тестированием ML-моделей и модельных сервисов для наших клиентов уже более четырех лет, и более трех из них я погружен в наши процессы QA.

Я попал в Альфу через проект компании KTS, над которым работал раньше, и первое время занимался ручным тестированием. За несколько лет прошел путь от линейного тестировщика до руководителя команды из 8 человек, и в этой статье рассказываю о своем опыте и делюсь советами, которые дал бы самому себе несколько лет назад.

Эта статья будет полезна:

  • тем, кто только начинает выстраивать процессы тестирования моделей;

  • начинающим тимлидам QA-команд до 10 человек;

  • тем, кто просто хочет познакомиться с примером организации QA-процесса с нуля.

Я поделюсь этапами развития команды, нашими подходами и инструментами. А ещё расскажу о том, почему принимались те или иные решения, и каких ошибок мы смогли избежать или не смогли.

Свои рассуждения в рамках этой статьи я поделю на две категории:

  • Проблема — практическая ситуация, для которой можно подобрать типовое решение.

  • Экзистенциальный вопрос — те задачи, на которые нет «правильных» ответов. Только проверка в реальных практических условиях. В основном это вопросы мотивации, работы с людьми или выбора меньшего из двух зол.

Оглавление:

Уровень 0: когда ты единственный тестировщик

Любой проект начинается с малого — с задач, из которых вырастают процессы и команды. Когда я попал в команду СИМ (система управления моделями), мне поставили задачу: обеспечить функциональное и нагрузочное тестирование ML-моделей перед их деплоем в прод.

До этого мне не приходилось работать с настолько верхнеуровневыми и ответственными задачами, и такая постановка стала для меня вызовом. В то же время я понял, что расширение зоны ответственности может стать моей точкой роста. Но здесь я не буду погружаться в детали своего эмоционального состояния, лучше сразу перейдем к сути.

На тот момент у нас было два типа моделей:

  • Batch — Python-скрипты, которые запускаются по расписанию в Airflow и работают с определённой выборкой клиентских данных (фичи). Результат работы такой модели — так называемый скор, предсказание склонности клиента к определенным продуктам банка. Сформированный скор скрипты записывали в БД Hadoop.

  • Online — классические web-сервисы с взаимодействием через REST API, работают в real-time и вызываются сервисами извне. Модель разворачивается на своём URL и принимает на вход JSON определенного формата. В зависимости от данных в запросе модель делает расчёт и возвращает результат также в формате JSON.

Сначала модель проходит обучение (трейн), в ходе которого она работает с тестовыми данными. Если результат трейна удовлетворяет Data Scientist’а, то на основе mlflow-эксперимента собирается образ модели, и она допускается до вычислений на реальных данных (инференса).

В дальнейшем появились и другие типы моделей…

…такие как:

  • stream-модели, читающие поток сообщений из Kafka;

  • модели с автопереобучением, способные автоматически запускать трейн без участия человека, основываясь на метриках качества и точности расчетов этой модели;

  • каскады — наборы последовательно или параллельно запускающихся моделей, в которых результат работы одной модели передается на вход последующей (управляется оркестратором моделей).

Важный факт: на функциональное тестирование любой из этих моделей установлен SLA в 8 рабочих часов.

Итак, в самом начале я был единственным тестировщиком в команде.

Batch-модели проверял вручную, а именно — занимался проверкой лога работы дага инференса в Airflow и корректности записи скора в Hadoop. Online-модели проверял по чеклистам, поскольку на поддержку тест-кейсов просто не хватило бы ресурса (да и поддерживать их, по сути, незачем). Первое время вёл все чек-листы в Confluence, но довольно быстро перешел на Postman-коллекции — это удобнее во всех планах.

Нагрузочное тестирование проводил с помощью Locust. Работал он кривовато и не особо удобно, но более сложными инструментами пользоваться я ещё не умел, так как до этого не сталкивался с нагрузочным тестированием в принципе. В отчет по результатам тестов входила проверка утилизации ресурсов CP и RAM на дашборде в Grafana, а также проверка времени ответа модели при заданном RPS.

Основные критерии, которыми я мог руководствоваться для оценки своей работы — субъективное ощущение, что справляюсь с распределением и приоритезацией задач, и скорость выполнения этих задач.

Проблема: ресурсов категорически не хватало, отлаженный процесс отсутствовал, а поток моделей рос.

С таким подходом я и тестировал модели, а в свободное время начал разрабатывать прототип полноценной методики тестирования, но количество задач постоянно увеличивалось. Тестировать за день более двух моделей одновременно — с функциональным и нагрузочным тестированием — было просто невозможно. Я работал в режиме реверса. 

Решение казалось довольно очевидным. Нужно было расширять команду тестирования, чтобы высвободить ресурс на развитие более зрелых процессов.

Уровень 1: команда и её расширение

Согласовав решение расширять команду, я собрал профиль кандидата: нужные технические навыки, опыт, личные качества, мотивация и ценности. Этот профиль служил фильтром на начальных этапах отбора, и помогал оценить соответствие кандидатов требованиям.

Таблица 1. Профиль кандидата при найме

Таблица 1. Профиль кандидата при найме

О процессе собеседования и найме можно писать отдельную статью, так что не буду на них зацикливаться. Здесь я хочу дать две коротких рекомендации:

  1. Нет принципиальной разницы, когда проводить техническое интервью — до или после беседы о софт-скиллах. Кандидат может одинаково не подойти как по хардам, так и по софтам.

  2. Даже после прохождения HR-фильтра будьте готовы к тому, что от большинства соискателей придется отказаться — лучше вас никто не знает, какой кандидат реально подходит проекту и команде. В моем случае доля подходящих кандидатов составила около 15%.

После найма 4 тестеров (1 нагрузочный и 3 авто) у меня появилось время на развитие процессов QA.

С приходом специалиста по нагрузочному тестированию началась разработка отдельной методики нагрузочного тестирования. Мы перешли от упрощенных решений к систематизированному подходу, позволяющему контролировать качество моделей через гибкую настройку параметров нагрузки (RPS, динамика роста, длительность) и использование разнообразных метрик и отчетов. Это стало фундаментом для отказа от принципа «быстрее и проще» в пользу стратегии «сложнее, но качественнее».

И мы бы сразу провели ещё целую пачку реформ, но помимо тестирования моделей у нас появился параллельный проект (сервис для вывода моделей), который также подъедал ресурсы тестировщиков.

Возник экзистенциальный вопрос: кросс-функциональная команда или специализация по проектам?

Работа над двумя параллельными проектами потребовала стратегического выбора. Я взвесил два подхода: либо разделять команду по проектам (глубокая специализация), либо сделать её кросс-функциональной. Каждый подход имел свои преимущества.

Кросс-функциональность и её плюсы:

  • взаимозаменяемость членов команды между проектами;

  • отсутствие единого держателя знаний и компетенций (нет незаменимых);

  • более широкий стек у каждого сотрудника, более активная прокачка в разные стороны;

  • более разноплановые задачи, которые не дают заскучать;

  • более широкий взгляд на продукт в целом.

Специализация на одном проекте и её плюсы:

  • отсутствие скачков между контекстами, больше стабильности для каждого сотрудника и меньше стресса;

  • возможность более глубокого освоения определенного стека;

  • более быстрый онбординг новичков.

Ключевым фактором принятия решения стала взаимозаменяемость, т.к. при ограниченных людских ресурсах и неудачном стечении обстоятельств (например, при сверхвысоком объёме моделей на тест и одновременном отпуске/болезни одного тестера) мы могли остаться без необходимого количества ресурсов в одной из команд. Поэтому был сделан выбор в пользу кросс-функциональности.

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

Уровень 2: рост и развитие

С ростом команды появилась необходимость умело ей управлять. Первым же делом мы ввели еженедельные ретроспективы. Но на одних ретроспективах далеко не уедешь. Было много вопросов, что нужно для развития и с какой стороны к нему подходить.

Я не стал самостоятельно набивать шишки — это долго и дорого — а решил пройти курс повышения квалификации. За несколько месяцев я прошел курс в школе тест-менеджеров от «Лаборатории качества», в ходе изучения которого внедрял лучшие практики, подходы и инструменты в работу своей команды.

Для начала сделал онбординг более зрелым и быстрым. Получение доступов в банкинге — процесс и без того мучительный и небыстрый, а вдобавок и документация по онбордингу у нас была не вполне адаптированной под новичка. Казалось, что легче созвониться и ответить на вопросы по доступам голосом, чем писать все подробно. Но на практике все оказалось иначе.

Я расписал онбординг во всех деталях, добавил скринов и даже видео-гайды для тех, кому легче воспринимать информацию в формате видосика. В итоге все стали тратить меньше времени на созвоны, и процесс не стопорился каждые полчаса. И срок от момента выхода на работу до первых боевых задач сократился с двух недель до одной.

Также мы добавили в нашу работу следующие нововведения:

  • ввели матрицу скиллов для более качественного подбора сотрудников;

  • для оперативного решения проблем на начальных стадиях начали регулярно собирать обратную связь, причем не только от членов команды, но и от смежных команд;

  • определили ключевые метрики для оценки качества тестирования (скорость тестирования, количество дефектов на проде, процент минимального тестового покрытия);

  • добавили встречи 1-to-1 и практику кросс-ревью тестов;

  • выявили основную и самая критичную проблему в текущем процессе тестирования — скорость прохождения ФТ;

  • для решения сложных и повторяющихся проблем стали применять метод «5 почему». На нем хочу остановиться чуть подробнее.

Метод «5 почему» интуитивно понятен, относительно прост и быстро применим на практике. Каждую идею и проблему мы пропускаем через фильтр уточняющих вопросов, раскрывающих их суть, и в итоге получаем список истинных причин, которые детализировать глубже уже невозможно. С этими причинами мы и работаем дальше.

Например, одна из проблем заключалась в возникновении непонятных багов, дебаг которых занимал много времени и затягивал процесс вывода модели в прод. Назовем её проблемой первого уровня. Почему она возникает? Потому что нет глубокого понимания, как работает система. Это уже «подпричина второго уровня».

Дальше тем же способом выявляем «подпричины третьего уровня»:

  1. Нет документации/неочевидно, где она лежит.

  2. Непонятно, кто может проконсультировать/пошарить экспертизу.

  3. Не хватает знания стека технологий.

Это уже понятные проблемы, с которыми можно работать. А именно:

  1. Узнать, где есть документы, и поставить задачи на разработку недостающих инструкций.

  2. Уточнить у коллег их зоны ответственности и компетенции.

  3. Изучить стек технологий самостоятельно или пройти релевантный курс.

В нашем примере докопаться до сути удалось всего за два «почему». Практика показывает, что даже самые запутанные проблемы можно разобрать за пять итераций. Поэтому я смело рекомендую вам внедрять этот подход, если перед вами стоят задачи высокого уровня абстракции.

Вернемся к нашей истории. На этапе развития передо мной встала новая трудность, а точнее, новый экзистенциальный вопрос: как правильно делегировать задачи? Как распределять их таким образом, чтобы команда не выгорала, а развивалась и училась работать самостоятельно?

Казалось бы, проще всего железной рукой раскидать тикеты и ждать их выполнения. Жаль, конечно, но такой подход работает только в армии, и то не всегда. Нельзя ставить задачи наугад. Они должны быть осмысленными, посильными и — в идеале — интересными человеку, который их выполняет.

Руководитель, который недостаточно понимает своих подчиненных и их скиллы, рискует потратить свое же время на разбор возникших вследствие проблем.

Чтобы лучше понимать свою команду, я составил профиль для каждого сотрудника на основе 1-to-1 встреч. В них собрал информацию о мотивации, потребностях в развитии, определил уровень профессиональной зрелости и, соответственно, адаптировал стиль управления: новичкам давал больше поддержки, объяснения и контроля выполнения, а опытным — свободу принятия решений. Это помогло предотвратить лишний микроменеджмент и снизить риски от возможных ошибок менее опытных коллег.

Основные выводы, которые хочу подсветить:

  1. больше делегирования — залог успеха любого руководителя;

  2. необходимое (но не достаточное) условие оптимизации работы в команде — знание слабых и сильных сторон своих сотрудников;

  3. зона ответственности сотрудника не должна опережать его скиллы, т.е. не стоит делегировать задачи тем коллегам, в подготовке которых вы еще сомневаетесь (здесь как с обгоном на трассе: не уверен на 100 % — не обгоняй).

Таблица 2. Пример определения мотивационных факторов

Таблица 2. Пример определения мотивационных факторов
Таблица 3. Пример определения стиля руководства

Таблица 3. Пример определения стиля руководства

Уровень 3: автоматизация тестов

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

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

В основу архитектуры наших автотестов лёг код, из которого автоматически генерируется документация в виде чек-листов с помощью AllureTestOps. То есть автотесты уже сами по себе являлись основой для тестовой документации. Так мы отказались от её ручной поддержки: можно просто писать автотесты, а всё остальное сделает автоматическая генерация тестов из кода. Круто!

Наш стек: Python + Allure + Requests + Pytest.

Флоу тестов: тесты вызываются в Jenkins при сборке инференса модели, достаточно отметить соответствующий чекбокс в пайплайне. Лог автотеста можно посмотреть в даге Airflow.

Дальше автоматически генерируется отчёт о результатах работы автотестов в виде Allure Report, и с помощью утилиты Allurectl он отправляется на облачный сервер Allure Testops. Там на основе отчета генерируется тестовая документация к данной модели.

По сути, автотесты проверяют REST API модели через комплекс сценариев, реализованных на pytest с интеграцией Allure для отчетности. Основная работа происходит по следующей схеме:

1. Подготовка данных и запросов.

Каждый тест начинается с формирования тестового payload (с использованием RequestData.payload), где:

  • для негативных сценариев специально удаляются обязательные/опциональные поля через Helper.delete_key();

  • данные копируются через dc().

2. Отправка и валидация.

Через кастомный ApiClient.post отправляются POST-запросы на заданный URL, после чего:

  • проверяются статус-коды ответа;

  • для успешных запросов ответ проходит валидацию через model_income_t0_validator();

  • все действия логируются через logger_fixture.

3. Параметризованное тестирование.

Особенность реализации — использование @pytest.nank.parametrize для:

  • последовательной проверки всех обязательных полей (RequestRequirements_REQUIRED_KEYS);

  • верификации опциональных полей (RequestRequirements_OPTIONAL_KEYS).

4. Тесты интегрированы в инфраструктуру через:

  • кастомные маркеры pytest (@pytest.nank.*) для классификации тестов;

  • Allure-аннотации (@allure.title, @allure.fastup) для детализированной отчетности;

  • фикстуры (например, logger_fixture) для сквозного логирования.

Такой подход обеспечивает проверку как happy path (успешных сценариев), так и edge cases (обработки ошибок), что соответствует принципам тестирования API. Все тесты независимы благодаря изолированной подготовке данных и могут выполняться как по отдельности, так и в составе test suite.

А как выбрать тесты под автоматизацию?

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

Функционал для автотестов должен быть более-менее стабильным. Лучше всего автоматизировать те тесты, которые цикл от цикла не находят ошибок, чтобы освободить время под ручное тестирование более забагованной функциональности (тех мест, где чаще происходят изменения в коде). 

Неправильный выбор функциональности под автотесты может привести к следующим проблемам:

  • автоматизация не экономит, а крадет еще больше времени на поддержку тестов в актуальном состоянии;

  • тестирование становится дороже, при этом его метрики качества остаются прежними или деградируют.

К этому этапу медианное время на разработку коллекции составляло 4 ч, а ревью коллекции — 1 ч. Мы выделили следующие плюсы автоматизации на фоне Postman:

  1. Можно не поддерживать тестовую документацию за счет интеграции с Allure TestOps, это экономит время и дает более удобную и полную отчетность в Allure в сравнении с Postman.

  2. Нет ограничения на количество пользователей, в отличие от бесплатной версии Postman.

  3. Менее ограниченная логика — раньше сложные сценарии (условия, циклы) требовали написания скриптов на JS, что не подходило к нашему стеку.

  4. Гибкость — в Postman было сложно реализовать динамическую параметризацию (например, генерацию данных на лету).

  5. Если тест в Postman падал, разобраться в причине было сложнее, чем в случае с кодом.

Однако мы не отказались от использования Postman полностью. Оставили его для быстрых проверок и ручного тестирования там, где это необходимо.

Также часто для оценки выгоды от автоматизации стали использовать следующую формулу (чем выше ROI, тем выгоднее):

  1. Определяем время на прохождение ручного теста.

  2. Умножаем на количество прохождений тестов в релиз/спринт.

  3. Прибавляем буферное время (сколько закладываем на риски), получаем время, которое будет потрачено на ручное тестирование.

  4. Оцениваем время на разработку автотестов.

  5. К оценке прибавляем время на анализ результатов автотеста, умноженное на количество прохождений автотестов в спринт/релиз.

  6. Прибавляем буферное время, получаем время, которое будет потрачено на автоматическое тестирование.

  7. Сравниваем результаты, полученные в п.3 и п.6. Если п.6 меньше, то автоматизацию считаем выгодной.

Рис. 1. Пример кода автотеста для онлайн модели

Рис. 1. Пример кода автотеста для онлайн модели
Рис. 2. Allure-отчетность

Рис. 2. Allure-отчетность

После автоматизации тестов для online-моделей мы взялись за тесты для batch-моделей. 

Целей было две: 

  1. Автоматически проверять запись скоров в БД.

  2. Добавить проверку работы модели на невалидных синтетических данных, чтобы увеличить тестовое покрытие.

Архитектура решения следующая: сначала запускается пайплайн инференса batch-модели, и на этом же этапе происходит выкачка библиотеки с синтетическими тестами из Artifactory. После завершения инференса происходит проверка ключа autotests=true в конфиге с моделью. Далее проверяется соответствие кода модели стандартному шаблону и запускается код автотеста. Результаты работы выводятся в лог. Если модель не соответствует шаблону или ключ autotests=false отсутствует, то тесты не запускаются.

  1. Тесты запускаются из тестовой ветки проекта в инференс-пайплайне Jenkins.

  2. После загрузки данных read_data берется несколько строк из датафрейма pandas/pyspark и генерируем синтетические данные для проверки негативных кейсов (пустые строки, NaN, неверный тип данных и т.д.).

  3. На этапах препроцессинга и инференса используем сгенерированные синтетические данные по тестам.

  4. По завершении тестов результаты выводятся в лог пайплайна.

  5. Очищаются тестовые данные.

Рис. 3 Архитектура batch-автотеста

Рис. 3 Архитектура batch-автотеста

Виды проверок:

  1. Проверка класса на наличие необходимых методов.

  2. Проверка класса на тип входных аргументов.

  3. Проверка скоров в БД на граничные значения класса эквивалентности.

  4. Проверка скоров в БД на валидный тип данных.

  5. Проверка на невалидных типах данных (неверный тип данных в фичах, пустые строки, Nan, дубли).

Проблема + Экзистенциальный вопрос. Как и зачем?

Необходимость разработки таких тестов сразу встала под вопрос. Зачем нам тратить время на разработку, если разница во времени между ручной и автоматической проверками не так велика?

Тут мы смотрим на перспективу: один раз пишем код для шаблонной модели, и он работает всегда в любое время суток, достаточно datascientist’у заполнить конфиг для его работы. К тому же, у синтетических тестов в проверке работы модели есть преимущество: если в БД запишутся невалидные данные, то модель не упадет, а выдаст ошибку.

Планы на будущее

До конца 2025 года мы планируем следующие улучшения:

  1. Прямо сейчас мы разрабатываем автотесты для AutoML-моделей. Используя наработки для batch-моделей, мы можем покрыть тестами и самообучающиеся модели.

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

  3. Будем развивать индивидуальные планы развития (ИПР) и матрицу грейдов. Это поможет сделать развитие команды более прогнозируемым и даст каждому члену команды понимание, как ему развиваться и какие именно скиллы ему для этого нужны.

Выводы

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

  1. Угнаться и за процессами, и за закрытыми тикетами в одиночку очень тяжело. Разумнее будет расширить команду и заняться налаживанием процессов.

  2. Делегирование — ключ к росту. Но только осмысленное и структурированное, с учетом навыков и мотивации членов команды.

  3. Автоматизация требует анализа эффективности (Например ROI). Если автотест «дороже» и «медленнее» ручного — стоит задуматься о его целесообразности.

  4. Автоматизация экономит время, но требует правильного подхода к реализации.

  5. Непрерывное улучшение процессов — залог развития.

  6. Сотрудничество с другими командами помогает решать сложные технические задачи.

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

Автор: KValen

Источник

Rambler's Top100