Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Moscow Python Meetup №88. Александр Винокуров (Группа компаний "Самолет", руководитель группы разработки). Практическая сторона тестов

Moscow Python Meetup №88. Александр Винокуров (Группа компаний "Самолет", руководитель группы разработки). Практическая сторона тестов

Делюсь личным опытом болей и радостей жизни с тестами и без. Обсудим лучшие и худшие практики. Покурим вместе код.

Видео: https://moscowpython.ru/meetup/88/tests-practical-side/

Moscow Python: http://moscowpython.ru
Курсы Learn Python: http://learn.python.ru
Moscow Python Podcast: http://podcast.python.ru
Заявки на доклады: https://bit.ly/mp-speaker

Moscow Python Meetup

February 28, 2024
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Компания Samolet Крупный девелопер и PropTech компания Мы нацелены на

    автоматизацию и цифровизацию процессов на стройке 50+ внутренних проектов с использованием Django и FastApi Много проектов со сложными бизнес процессами 2
  2. История номер раз В нашей экосистеме есть несколько сервисов и

    нам нужно по неким триггерам синхронизировать данные между ними. Сам процесс написан достаточно сложно и он объективно достаточно не простой. Оставим архитектуру этого решения за скобками — рассмотрим ситуацию AS IS. 4
  3. Service A Service B node 1 node 2 sub-node 2.1

    sub-node 1.1 sub-node 1.2 sub-node 2.2 sub-node 2.3 node 1 node 3 sub-node 1.3 sub-node 3.1 sub-node 3.2 sub-node 1.2 Синхронизация ~10 000 нод ~3 000 строк кода
  4. Продолжение В какой-то момент мы стали замечать, что один из

    разработчиков тратит примерно 30-50% времени спринта на исправление багов в этой части проекта. Если потрогать любой участок кода из этих примерно 3000 строк, то вероятность того, что создашь баг была крайне велика. Это связано с несколькими факторами: • объективно сложный код • не простые древовидные структуры данных • нулевое покрытие тестами 6
  5. Это больно Количество времени на “раскуривание” этой истории, плюс постоянные

    авралы из-за того, что наша система работает нестабильно и является достаточно хрупкой постоянно порождали у меня и всей команды болевой синдром. Ситуация драматически изменилась, когда мы целенаправленно уделили время написанию тестов на эту части системы. По сути можно было работать в нескольких направлениях — либо понижая сложность системы, либо же увеличивая покрытие тестами. В итоге мы написали примерно 2 000 строк кода тестов и новый код перестал ломать существующий. 7
  6. Чему я научился 1 Важно, чтобы на проекте был человек,

    который знает “как правильно” и способный отстаивать, в том числе и через опыт и цифры, правильные инженерные практики. Например ценность и важность тестов, особенно на сложных или хрупких участках кода. 8
  7. Чему я научился 1 Важно, чтобы на проекте был человек,

    который знает “как правильно” и способный отстаивать, в том числе и через опыт и цифры, правильные инженерные практики. Например ценность и важность тестов, особенно на сложных или хрупких участках кода. 2 Стоимость отсутствия тестов на старте разработки не очень велика, но, чем дальше вы идете, чем сложнее становится бизнес- процесс, тем дороже вам и в плане морали, и в плане денег вам обойдется это приключение. 9
  8. Чему я научился 1 Важно, чтобы на проекте был человек,

    который знает “как правильно” и способный отстаивать, в том числе и через опыт и цифры, правильные инженерные практики. Например ценность и важность тестов, особенно на сложных или хрупких участках кода. 2 Стоимость отсутствия тестов на старте разработки не очень велика, но, чем дальше вы идете, чем сложнее становится бизнес- процесс, тем дороже вам и в плане морали, и в плане денег вам обойдется это приключение. 3 Постоянная спешка и стресс вводит вас в неразрешимую петлю. В какой-то момент нужно сделать стоп и начать разбираться. 10
  9. История номер два Важно, чтобы все члены команды понимали и

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

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

    моей истории номер раз. Потрогав какой-то кусок кода, который был написан, возможно, не вами и, возможно, несколько лет назад вы будете чувствовать себя более уверенно если код был покрыт тестами. Да, у нас есть прекрасные люди под названием тестировщики, которые находят баги в наших приложениях, но, чем больше у нас линий обороны, тем более качественный продукт мы выпускаем и тем больше можем гордиться результатами своей работы. А это важная часть мотивации. 14
  12. Взгляд на проект со стороны потребителя Тесты позволяют посмотреть на

    ваш код с другой стороны — со стороны потребителя, иногда это важно и полезно. Плюс вы всегда сможете открыть тест и посмотреть что ваша, например, ручка (для интеграционных тестов) ожидает на вход и какой будет выход. Чем с большего количества перспектив вы понимаете систему тем лучшим специалистом вы являетесь и тем более комплексные задачи можете решать. 15
  13. Качественная и предсказуемая разработка (TLD) С помощью тестов можно быть

    достаточно уверенным в том, что вы разрабатываете. Задачи могут быть достаточно сложными и много-составными и можно сдать фичу, работающую с одной стороны и не работающую с другой. Как бы мы не работали мы всегда имеем ограничение человеческого мозга — кошелек Миллера, поэтому покрыв какую- то часть нового функционала тестами мы забываем об этом кусочке и можем сосредоточиться на другом. 16
  14. Качественная и предсказуемая разработка (TLD) С тестами то же самое

    — покрыв тестами одну часть, можно забыть о ней — отдать на откуп автоматизации и сосредоточиться на другом кусочке. Это сродни разделению программы на слои — трудно работать со всем и сразу, никто не любит огромных лапшичных кусков кода, которые делают все, намного приятнее работать с кодом, нарезанным слоями, т.к. мы работает только с частью приложения, а не со всем. 17
  15. TDL vs TDD При использовании TDL вы пишете маленький участок

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

    списка товаров. Для начала вам нужно описать базовые бизнес правила в коде, повесить ручку на какой-то URL, позаботиться о сериализации. После написания этих базовых шагов, вы можете написать тест, который: • создает товар в БД • делает запрос на указанный URL • проверяет корректность ответа 19
  17. Пример TDL Позже вы добавляете фильтры для вашей ручки списка

    товаров. Например вам нужно уметь получать товары конкретной категории. Как только вы реализовали эту бизнес логику в коде, вы пишете тест, который: • создает товар категорий А и Б в БД • делает запрос по указанному URL с фильтром по категории А • проверяет что вернулся товар категории А и не вернулся товар категории Б 20
  18. Пример TDL Затем вы добавляете возможность поиска по названию товара

    и пишете тест, который Создает в БД два товара — мишку и зайчика Дергает указанную ручку и просит вернуть только мишку Проверяется что ручка вернула мишку и не вернула зайчика 21
  19. 24

  20. 25

  21. 26

  22. 27

  23. Хорошие практики — проверяем одну штуку за раз Каждый тест

    должен проверять одну штуку, если тест проверяет много штук, то такой тест становится сложнее поддерживать. Для интеграционных тестов — один тест дергает одну ручку. Для юнит тестов — один тест проверяет одну функцию или метод. 28
  24. Хорошие практики — общие моки Файл настроек tests.py Хорошо, когда

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

  26. Хорошие практики — минималистичные фикстуры Не нужно создавать ненужных сущностей

    — лучше создать один объект как частный случай создания множества. Это упрощает понимание и поддержание тестов. Помните о правиле — пишем код мы один раз, а читаем множество. Создание лишних сущностей усложняет поддержку кодовой базы в длительной перспективе. Проигрыш начинается и по поддержке тестов, и по времени их исполнения. Огромный тест будет не читаем через пол-года. Так-же как и огромная фикстура, в которую каждый разработчик постепенно складывает все. В итоге мы имеем огромную помойку, которую уже невозможно разгрести из-за большой связанности и проще сделать заново. 32
  27. 33

  28. 34

  29. Хорошие практики — минималистичные фабрики Если писать фабрики их тоже

    нужно поддерживать - чтобы под капотом не создавалось ненужных сущностей / связей. Если создавать фабрику, которая под капотом создает другую фабрику и там есть сайд эффект, который создает еще пачку сущностей, то тесты неминуемо начнут проходить все дольше и дольше. Плюс в какой-то момент наш кошелек Миллера переполняется и код превращается в помойку. В рамках нашего проекта мы пришли вот к этим договоренностям: • пишем минималистичные фабрики в том же приложении, где хранятся модели • фабрики хранятся в директории приложения в файле factories.py или директории factories • фабрики минималистичны — по-умолчанию заполняем только обязательные поля. Это нужно для упрощения тестов и ускорения их работы. Все необязательные поля заполняем уже в фикстурах на конкретный тест / группу тестов 35
  30. Хорошие практики — покрытие Если в вашем проекте не большое

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

    В своей практике я пришел к правилу — если прилетает баг на какой-то участок кода, нужно исправить этот участок и написать тест, который бы ломался на не исправленном участке кода (до того, как мы написали фикс) и проходил бы на исправленном (после фикса). Это позволит быть уверенным в том, что текущая правка устраняет баг и в том, что этот баг не воспризведется в будущем. 37
  32. Важность юнит тестов Юнит тесты важно писать для сложной бизнес

    логики. Это облегчит вам жизнь в будущем — эта сложная бизнес логика не будет случайно кем-то сломана, плюс вы сами намного лучше ее поймете, если подергаете функции / методы с разными входными и выходными значениями. Плюс у вас останутся перед глазами примеры входных и выходных значений, в которые можно будет посмотреть в любой момент и понять как работает ваша сложная бизнес-логика. 38
  33. Важность юнит тестов — пример из жизни Я работал на

    проекте, который делал достаточно сложные вычисления временных рядов. Это был проект из мира IOT. У нас было условие, что мы доверяем входным данным, а все “плохие” значения фильтруем на уровне кода. В итоге у нас был ряд очень сложных и функций с неочевидным поведением и я специально покрывал их юнит-тестами. Это позволило: • по входным и выходным параметрам юнит-тестов понимать как именно работает этот сложный код • быть уверенным в том, что код работает верно 39
  34. Плохо Хорошо Покрытие основных бизнес-кейсов Фабрики с сайд- эффектами Тесты

    зависят друг от друга Огромные фикстуры Минималистичные фабрики Общие моки Минималистичные фикстуры Покрытие багов 31