Comments 32
Хорошая история успеха. Поставленна полезная цель. И метрики не забыли и коллаборации с другими командами есть. Кейс конфетка для резюме.
В вашем случае, какие из применёных практик стоило бы подключить с самого начала проекта?
Добрый день.
1. sleep() - зло. Наверное у меня проф деформация, но я как только вижу в коде sleep() сразу задаю вопрос зачем и почему.
2. Почти сразу запускать тесты в параллель. В нашем случае через xdist. Это сразу будет приучать как удобнее и корректнее разделять тестовые данные, как правильно прописывать фикстуры для pytest и тп
Да, примерно так всё и выглядит: надо запускать тесты параллельно, но что-то ломается. и начинаешь ковыряться.
Я только одного не понял, а зачем самописный retry? Есть ведь библиотечные варианты.
Я смотрел библиотеки которые реализовывают retry, но после обсуждения с командой решили от них отказаться. Тот пример что в статье, это самый простой вариант которые удобно показывать в статье.
На самом деле у нас много кастомных retry функций которые основаны на какой либо логике сервиса. Да получается много костылей и самописного кода, но это удобнее чем пытаться делать override или как то обойти узкие места внешних библиотек.
Из плюсов, такой код как в примере - очень легко пишется
Мрак, конечно.
Я только не понял проблему с файлами и количеством тестов. Разве тесты не по Танцам в сьюты собираются? Какая разница сколько там в файле?
Ну а вообще умные ожидания - это как бы база. Но в современных реалиях лучше сразу использовать асинхронный подход.
А вообще, никто не пытается решить ключевую проблему.
Как вы выбираете какие именно тесты будут гоняться для комитета?
Никто не хочет думать и просто выбирает все тесты, что есть. Но в этом нет ни смысла, ни логики. Из всего количества в 95% случаев нужно прогнать только тесты связанные с затронутым функционалом. А остальная масса - просто пожирает время. Не неся в себе никакой информации. И так очевидно, что изменения, например админки или бэкофиса, никак не могут повлиять на процессинг платежей или MFA. Но в подавляющем большинстве тесты все равно будут запущены и результаты получены. Только как какой в этом толк. Начинать всегда нужно с правильных механик, что и когда запускать. На коммит - достаточно проверить непосредственный модуль и смежные. А полный прогон на merge feature branch.
Это, конечно, не отменяет важности проделанных изменений. Но подход не с той стороны. Большую часть проблемы можно было решить гораздо меньшими усилиями.
Подскажите пожалуйста инструмент, который по строчке кода найдёт все модули на которые она могла повлиять. Чтобы он работал для любой архитектуры, даже если там зависимости не только через импорты.
Для этого придуманы таги.
Ставишь нужные таги исходя из архитектуры и потом запускаешь сьюты по тагам. Тут конечно нужно думать, архитектуру читать. А еще ее должен кто-то написать. А silver bullet - не существует. Хочу не думать и чтобы все за меня получилось. Так не бывает. Кто-то верит, что AI все сможет. Если готовы так рискнуть, можете его попробовать сейчас достаточно инструментов устраивают AI для этих целей. Строится интеграция с системами Тест Менеджмента, и AI за вас соберет необходимые сьюты.
Теги то поставить просто, а вот автоматически выбирать какие из них запустить на изменения это уже проблема. Не запускать тесты, если поменялся только readme.md можно, а вот по коду делить чаще всего не выйдет.
Идея не гонять все тесты каждый раз хороша, но имплементации слишком сложна.
Я бы не сказал, что это сложно. Но это требует работы. Которую, с одной стороны делать нужно, а с другой стороны делать не хотят. И платить за нее не хотят.
Это требует грамотного построения процеса и адекватной поддержки документации. Если задачи сгруппированы по features/user stories, залинкованы друг с другом согласно Software Architecture. Feature/user story включает позадачи не только на разработку но и на написание/обновление тестов на всех уровнях, поддержку документации, составление тест-плана, и т.д. То никаких проблем собрал release notes, тесты посмотрел по tags, эквивалентным фичам, можно парсить заколовок feature branch и брать оттуда, если ведется конвенция заголовок. Я ведь надеюсь она ведется, хотя бы для интеграции Task Management c репозиторием. И получаешь тест-ран без проблем в несколько кликов или вообще автоматизировано.
Если в документацию не вкладываться, на Архитектуру и прочую документацию не обращать внимания, на планирование и согласование тестов время не выделять. И по менеджменту проекта только делать вид, а не реально оперировать тикетами, вести релиз менеджмент. То да, сделать эффективное тестирование - нереально.
Тестирование, всегда опирается на входные данные в виде документации(требования, архитектуру, описание интерфейсов), если в это не вкладываться - тестирование не имеет смысла. Потому что задача тестирования, оценить степень готовности продукта и риск его релиза в текущем состоянии. А без входных данных невозможно измерить объем работ и изменений. И любой результат тест репорта - будет просто сферическим числом в вакууме. По которому абсолютно ничего нельзя сказать и измерить.
Мы точно говорим про автоматический запуск тестов на CI на каждый pull request?
Автор решал проблему именно эту проблему.
То никаких проблем собрал release notes,
Выглядит так, как будто у вас есть релизы и вы гоняет тесты перед датой релиза. Это проигрышная стратегия для бизнеса. Хорошо когда CICD и между началом работы и деплоем в прод происходит пара часов. Это правда будет подороже создания хороших тестсьютов.
Так вроде в этом и вопрос. Что нет смысла запускать полный тест-пьют на каждый пул-реквест. Для этого и есть слои тестирования. Ну пул-реквест - запускаются только необходимое подмножество. На мерж в фича-бренс другое подмножество. На мерж в main - третье подмножество всех тестов.
Ну и нелохо было бы привести аргументы, вашим утверждениям, про стоимость для бизнеса. А то они не выглядят убедительно. Может для какого-то отдельного бизнеса так и есть. Но бизнесов много и все они разные. Многие не терпят такого подхода, просто из-за регулятор.
С тем чтобы запускать только нужные тесты, я не спорю.
Я просто не знаю инструментов, которые смогут тесты разделить правильно. А чтобы это делать на уровне людей, нужно чтобы все писатели тестов хорошо знали всю систему и последние изменения в ней.
Стоимость для бизнеса складывается из затрат на поддержание какие тесты запускать при изменениях и пропущенные ошибки из-за того, что нужные тесты не запустили раньше.
Все эти тест-сьюты они критичны для ручного тестирования, потом, что оно не масштабируется. Для авто тестов можно достаточно быстро добавить железа.
Просто ускорить тесты намного проще. Это можно сделать один раз. При этом это может сделать даже человек, который не имеет глубоких знаний в бизнес логике и архитектуре этого проекта. Например человек который только пришёл на проект, как автор поста.
Нет, не достаточно. Все подобные системы заканчивают жизнь одинаково. 40 тысяч тестов, все зеленые. А баги все равно вылазят на продакшене. Понимание, какие тесты нужно гонять, а какие - нет это ключ к успешном проекту. Если нет такой цели, то нужно понимать, что проект проживет по инерции какое-то время, а потом загнется. А с подходом давайте гонять все - это тупик.
Архитектуру перестраивают, требования меняются, бизнес логика меняется, а тесты остаются. В них уже смысла может год как нет, может они написаны неправильно и дают false positive. Они просто поедают время.и вместо того, чтобы привести их в порядок покупают больше ресурсов в облаках. Потом приходит менеджер и спрашивает: "Почему такой бюджет на облака? Давайте сокращать." И ведь действительно в большинстве случаев никак не оправдан. Большая часть тестов, просто пожирает ресурсы на CI, пожирает время тестировщиков, DevOps, разработчиков на разбор упавших тестов. Которые вообще никаким боком к последним изменениям не относятся. Просто гоняются прицепом, потому что разбираться не хочется какие действительно нужны, а какие - нет.
Понимание, какие тесты нужно гонять, а какие - нет это ключ к успешном проекту. Если нет такой цели, то нужно понимать, что проект проживет по инерции какое-то время, а потом загнется. А с подходом давайте гонять все - это тупик.
Мне кажется мы о разном говорим. Поддержание тестов в актуальном состоянии и выбор какие именно тесты запустить на каждом изменении это две разные задачи, да у них есть некоторая похожесть.
Логику по выбору тестов которые запустить на ревью нужно написать и её нужно поддерживать. И я утверждаю, что это - сложная задача. Что-то можно сделать через юниты и тесты контрактов на уровне модуля. Можно построить граф зависимостей по коду и тестировать связанные модули (все тесты для этих модулей). Но кроме этого могут быть зависимости через базу/очереди.
Разработка сложных проектов требует практик: Менять интерфейсы между модулями только осмысленно, выкидывать не используемый код.
Правильно. Только осмыслив свой продукт можно принимать информированные решения как по изменению продукта, так и по изменению тестов. Эта же осведомленность позволяет выстраивать правильные сьюты и запускать нужные тесты при нужных изменениях. Есть вполне себе цепочки. Требования, код, тесты. Test Management системы с автоматизированными traceability matrix. Те же Requirement в Gitlab, если просто и по быстрому. Если GitLab не хватает, есть другие системы, TestRail, TestMo, etc. Хотя последние изменения, довольно многообещающие и должны довести в ближайшем будущем GitLab до приемлемого уровня.
Если этого недостаточно, то есть системы для хранения документации. Всë есть, вопрос желания и готовности компании это использовать.
Ну и опять же не стоит недооценивать Tags.
У меня было несколько вполне успешных проектов, где мы использовали Tags для автоматизированного сбора тест сьютов.
Успешные проекты, где сьюты собирались на системе TestRail, которая тригерила test runs.
Были проекты с HP ALM и Siemens Polarion. Где все было завязано через требования. А любое изменение и тест на него был завязан на требование. Соответственно любой запуск тестов легко собирается по затронутой бизнес логике обозначеной в требованиях.
Для проектов связанных с регулятор. Medical/Automotive/Air-Spacecraft. Там без этих систем в принципе не сделаешь. Другие проекты больше чем на 20-30 человек тоже вполне могли бы их использовать и это сильно бы им все упростило. Ведь решения готовые использовать проще, чем выдумывать новое свое.
Бывают объективных причины, когда нужно что-то свое. Но ща 15 лет, я такого не встретил. Всегда на вопрос почему не использовать - ответ был мы про это просто не знали и уже сделали так, а переделывать не хочется.
Так что лучше потратиться на исследование вначале. Чем потом платить временем, за изобретение своего велосипеда.
Т.е. регресс придумали зря? Достаточно фичатеста? То что вы описываете это именно фичатест, а предугадать какое изменение на что может повлиять мало в каком проекте возможно в принципе, имхо.
Регрес на то и регрес. Что запускает не все что можно, а то что нужно. Если команды не знают свой продукт и не понимают к чему приведут их изменения, то грош им цена.
> Если команды не знают свой продукт и не понимают к чему приведут их изменения, то грош им цена.
А какая у вас позиция в компании и какого размера проект (в количестве работающих над ним людей)?
Чтобы втащить асинхронный код в старый репозиторий придется много чего переписать. Команда и бизнес не готовы были на это тратить время.
Но... Недавно все пришли к решению, что в старом репозитории много плохого кода и туда стало трудно вносить большие изменения. Поэтому решили написать с нули новый репозиторий согласно слоеной архитектуры и мигрировать в него.
Репозиторий и код уже готов, сейчас начинаем в нем работать. Думаю что через месяц или два напиши статью о том что получилось. Но там будет больше про архитектуру.
Когда начальный фреймворк написан плохо, всегда проблемы. Тестовые сценарии всегда должны быть отделены, чтобы как раз можно было перенести на новый фреймворк с минимальными усилиями.
Но часто об этом не думают. И пришли к этому благодаря оутсорс. Проект сделал, как быстрее. Все работает, а про дальнейшую поддержку уже никто не думает. Скорее всего это будет проблема других команд, которые придут скажут как все плохо и будут полностью переделывать.
По поводу "как выбирать тесты". Вопрос очень сложный потому что у нас моно репозиторий в котором находятся авто тесты для десятков сервисов (это только бэкенд / АПИ).
Сложность в том что для запуска Сервис1 могут понадобиться подключение к Сервис2 и Сервис3, а потом еще и данные из других сервисов.
Или к примеру, создание какой либо сущности может потребовать ожидания в 5-10 секунд, потому то эти изменения расскатываются по всей системе и там задействованы много других сервисов. При этом мы хотели бы проверить что изменения расскатились корректно и проверить их на критичных нам сервисах - для этого нужно сделать к ним запросы.
Пока мы пришли к тому, что сделали связь Service: test_folder + src_folder. Какждый сервис это отдельная цепочка гитлаб джоб: healthcheck -> smoke -> tests ->. Если в папке с авто тестами или в папке с исходным кодом были изменения - то запускается вся цепочка. Таким образом мы покрыли 85-95% случаев.
Есть пробел что папки которые содержат какие либо base tools или более низкие уровни фреймворка не покрыты подобными правилами. Но в подобных случаях мы просто руками запускаем критичные наборы тестов.
Конечно могут понадобиться другие сервисы.
Поэтому и существует пирамида тестирования.
Есть множество готовых сетов. Но каждая компания адаптирует под себя исходя из размера проекта, его степени зрелости и фазы, степени зрелости самой компании.
В общем случаем - создал Feature/User Story, создал сабтаски на разработку обновлениями кода, на обновление документации, на написание тестов на всех уровнях - Unit, Unit Integration, Subsystem, Subsystem Integration, System, System Integration, Acceptance. Тут уже количество уровней напрую зависит от Архитектуры и должно коррелироваться ей, может продукту и 1го уровня достаточно.
В сабтасках создаются feature branches, во все тесты просачивается tag с именем feature или любой другой идентификатор на усмотрение команды.
Теперь, магия. Запуск тестов на всех уровнях с данным тагом покрывает весь необходимый функционал.
Если же это не происходит, значит либо проблема с Архитектурой и документацией, что она не позволила сделать правильное решение какие тесты должны быть добавлены и обновлены, либо кто-то в команде не понимает, что происходит.
Опять же теперь есть инструменты с AI позволяющие помочь с данной идентификацией. На сколько им доверять - это уже вопрос отдельный.
Цепочка которую вы выбрали вполне себе может существовать. Но как раз она является проблемой. Это взгляд на продукт снизу вверх, индуктивный. При рассмотрении продукта снизу вверх невозможно увидеть связей. Для этого нужно смотреть сверху вниз как пользователь. Пользователя интересует чтобы вызов действия приводил к ожидаемым последствиям, а не то все как именно оно реализовано. И именно рассмотрение сверху вниз через Архитектуру дает быстрое понимание задействованных модулей и интерфейсов. Но опять же при условии, что они нормально и актуально задокументированы.
Согласен с вами что наш подход не идеален.
То что вы описываете требует перестройки процессов в компании на всех уровнях( Начиная с самого верха и заканчивая рядовыми сотрудниками. А если добавить сюда десятки репозиториев и десятки команд....
На это никто не пойдет. Почему и другие подобные моменты - это тема достойна отдельной статьи))
Я могу обсуждать и рассказывать/пропагандировать "Как правильно". Но откровенно говоря это не входит в уровень моих обязанностей)
Я пробовал как-то xdist, но у меня не взлетело из-за того, что фикстуры, которые помечены как scope=session , исполнялись не один раз, а под каждый процесс. А БД-то у меня одна, в результате чего оно подало по дубликатам ключей. Как подобную проблему решали?
И какое количество тестов у вас, что изначально они целый час выполнялись?
Самое простое это запуск нужного вам кода в pytest hooks (там есть набор хуков который запускается до того как pytest передаст управление в отдельные сессии воркеров).
В этом хуке вам нужно будет проверить что вы находитесь в main воркере. Для этого у xdist есть набор переменных которые он устанавливает в окружение - https://pytest-xdist.readthedocs.io/en/stable/how-to.html
Дополнительный момент будет в том что созданное подключение вам нужно будет пробросить в ново-созданные сессии. Вот тут сходу не найду пример кода. Если не забуду завтра попробую накидать минимальный пример кода.
Плюс посмотреть/проверить как у вас обрабатываются множественные обращения к вашей базе.
Прочитал статью, и у меня сложилось впечатление, что на ключевые вопросы автор так и не ответил.
Например:
Почему именно тестовые данные вызывают такие проблемы с производительностью?
Почему нельзя генерировать тестовые данные на уровне
sessionи удалять их в конце, вместо создания отдельных наборов для каждого файла или сьюта?В чем смысл ночной генерации отдельных тестовых данных вместо их динамического создания перед запуском тестов?
Из-за чего тесты работают так медленно — в статье это практически не раскрыто?
Создается впечатление, что основное ускорение достигнуто за счет распараллеливания, тогда как фундаментальные причины медленной работы тестов остаются неясными. Было бы полезно увидеть более глубокий анализ первопричин и архитектурных ограничений, влияющих на скорость тестов.
Приведу пример из собственного опыта: я ускорял выполнение тестов в несколько раз просто за счет изменения scope у fixture для создания движка базы с function на session. В статье такие нюансы не рассматриваются, хотя во многих проектах именно неверный scope у фикстур является одной из ключевых причин медленного выполнения тестов.
Отчасти с вами согласен. Многие моменты можно было описать подробнее.
В случае наших тестов большую часть тест даты не получиться генерировать в сессионных фикстурах. Создание некоторых сущностей требует ожидания раскатывания изменений на несколько инстансов и запускает апдейт данных в других сервисах. Это может занимать до нескольких минут если считать полноценное обновление от отправки запроса до возможности работать с сущностью. Именно поэтому был создан Presetup который ночью производит преднастройку тест данных.
Почему тесты работали так медленно. Вот именно из тех причин что описаны: практически везде использовались sleep вместо ожиданий, все тесты гонялись в один поток, и фикстуры которые создавали тест данные которые требуют долгого ожидания (см. абзац выше). Это странно но исправление всего 3 этих пунктов серьезно улучшило скорость прогона авто тестов
Именно это не понятно, почему нельзя сгенировать моки этих сущностей? т.е. как обычно и должны работать unit тесты - тестировать только конкретный функционал и не тестировать задачи, которые не являются ответственностью данного сервиса.
Так это не unit тесты.
Это больше smoke и integration - так как все бизнес сущности крутятся на одном тест стенде. На данный момент нет тест окружений где поднята одна БД или какой либо микросевис от бекенда, и мы его полноценно тестируем.
Сам сервис который тестируем изначально тоже был по большей части монолитом.
Мы пока только начали двигаться в сторону распиливания монолоитов и изоляции каждого сервиса, что позволит тестирования микросервисов в отрыве от других сущностей. Вот тогда и можно будет применить моки и тп
Как ускорить автотесты на Python в Pytest в 8,5 раз