Comments 24
Хорошая история успеха. Поставленна полезная цель. И метрики не забыли и коллаборации с другими командами есть. Кейс конфетка для резюме.
В вашем случае, какие из применёных практик стоило бы подключить с самого начала проекта?
Добрый день.
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 и между началом работы и деплоем в прод происходит пара часов. Это правда будет подороже создания хороших тестсьютов.
Т.е. регресс придумали зря? Достаточно фичатеста? То что вы описываете это именно фичатест, а предугадать какое изменение на что может повлиять мало в каком проекте возможно в принципе, имхо.
Чтобы втащить асинхронный код в старый репозиторий придется много чего переписать. Команда и бизнес не готовы были на это тратить время.
Но... Недавно все пришли к решению, что в старом репозитории много плохого кода и туда стало трудно вносить большие изменения. Поэтому решили написать с нули новый репозиторий согласно слоеной архитектуры и мигрировать в него.
Репозиторий и код уже готов, сейчас начинаем в нем работать. Думаю что через месяц или два напиши статью о том что получилось. Но там будет больше про архитектуру.
Когда начальный фреймворк написан плохо, всегда проблемы. Тестовые сценарии всегда должны быть отделены, чтобы как раз можно было перенести на новый фреймворк с минимальными усилиями.
Но часто об этом не думают. И пришли к этому благодаря оутсорс. Проект сделал, как быстрее. Все работает, а про дальнейшую поддержку уже никто не думает. Скорее всего это будет проблема других команд, которые придут скажут как все плохо и будут полностью переделывать.
По поводу "как выбирать тесты". Вопрос очень сложный потому что у нас моно репозиторий в котором находятся авто тесты для десятков сервисов (это только бэкенд / АПИ).
Сложность в том что для запуска Сервис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 раз