<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.7-CVE.2020.14001">Jekyll</generator><link href="https://roch1990.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://roch1990.github.io/" rel="alternate" type="text/html" /><updated>2020-08-05T20:57:16+00:00</updated><id>https://roch1990.github.io/feed.xml</id><title type="html">Maintainable Python</title><subtitle>Улучшаем качество Вашего python кода, делаем devops штуки и наслаждаемся результатом вместе.</subtitle><entry><title type="html">В доме, который построил Джек</title><link href="https://roch1990.github.io/post/2020/08/06/v_dome_kotoryi_postoril_jack.html" rel="alternate" type="text/html" title="В доме, который построил Джек" /><published>2020-08-06T00:00:00+00:00</published><updated>2020-08-06T00:00:00+00:00</updated><id>https://roch1990.github.io/post/2020/08/06/v_dome_kotoryi_postoril_jack</id><content type="html" xml:base="https://roch1990.github.io/post/2020/08/06/v_dome_kotoryi_postoril_jack.html">&lt;p&gt;&lt;strong&gt;Предупреждение: Данный пост пропах субъективной оценкой и холиварными суждениями.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Вы часто задумывались про пакеты и их именование? А про именование модулей?&lt;/p&gt;

&lt;p&gt;Или может вы все складываете в модуль &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;utils&lt;/code&gt; - “и так сойдет”?&lt;/p&gt;

&lt;p&gt;А вишенкой на торте - в один модуль запихнуть с десяток классов, процедур и методов.&lt;/p&gt;

&lt;p&gt;Давайте вместе подумаем как сделать лучше, проще и удобней.&lt;/p&gt;

&lt;p&gt;Для начала давайте перестать все хранить в utils.&lt;/p&gt;

&lt;p&gt;Допустим, у нас есть в utils 3 модуля:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;date_operations.py&lt;/code&gt;  (как-то преобразовываем даты)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currency_operations.py&lt;/code&gt;  (преобразовываем валюты)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service_code_converter.py&lt;/code&gt;  (а тут какие-нибудь системные коды преобразуем в человеко-читаемое нечто)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Что можно с ними сделать? Для начала давайте разобъем на гипотетические классы, 
с учетом &lt;a href=&quot;https://roch1990.github.io/post/2020/07/29/parser_or_new_parser.html&quot;&gt;ухода от абстракций к реальным сущностям&lt;/a&gt;. Получим:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateClass&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CurrencyClass&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServiceCodesClass&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Почему добавляется Class - объясню позже.&lt;/p&gt;

&lt;p&gt;Отлично, теперь мы получили набор гипотетических классов, которые надо правильно разложить по пакетам и модулям.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateClass&lt;/code&gt; формируем пакет &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;date&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CurrencyClass&lt;/code&gt; формируем пакет &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currency&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServiceCodesClass&lt;/code&gt; формируем пакет &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service_codes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Теперь, импортируя эти классы, будем получать что-то вроде:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;currency&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyClass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Отлично, осталось разложить по правильным пакетам. Но как это сделать? 
Давайте попробуем пакет назвать по названию модуля. Получим что-то вроде:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;currency.currency&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyClass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Выглядит как в песне “В доме, который построил Джек”. В целом - не очень. Но что же делать в таком случае?&lt;/p&gt;

&lt;p&gt;А в таком случае - устанвливайте принадлежность данных модулей.&lt;/p&gt;

&lt;p&gt;Вот, например, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServiceCodesClass&lt;/code&gt; - скорее всего принадлежит обработке какого-нибудь ответа от смежного сервиса.&lt;/p&gt;

&lt;p&gt;А точнее результату его парсинга.&lt;/p&gt;

&lt;p&gt;Отлично, тогда можно сделать так:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;message.response.service_codes&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServiceCodesClass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CurrencyClass&lt;/code&gt;, впрочем как и &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateClass&lt;/code&gt; - скорее всего будут относиться к преобразованию данных в запросах/ответах:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;message.currency&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyClass&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;message.date&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateClass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Итого мы получили следующее:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;message.currency&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyClass&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;message.date&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateClass&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;message.response.service_codes&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServiceCodesClass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;И сравним с предыдущим вариантом:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;utils&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date_operations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currency_operations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service_codes_operations&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Что же нам это дало?&lt;/p&gt;

&lt;p&gt;1) мы теперь четко знаем принадлежность конкретного класса к задаче
2) мы четко видим структуру приложения
3) мы не можем накидать абстрактных методов и процедур в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;utils&lt;/code&gt;, как в помойку&lt;/p&gt;

&lt;p&gt;Итого, новый разработчик, придя на проект - сразу будет понимать 3 основных вещи:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Что это?&lt;/li&gt;
  &lt;li&gt;Зачем это?&lt;/li&gt;
  &lt;li&gt;Откуда это?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Более того, через полгода вам не придется шариться по utils, выискивая ту самую процедуру &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_id&lt;/code&gt; или &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format_all&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Да, я забыл рассказать почему название класса &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateClass&lt;/code&gt;, а не &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Date&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;По хорошему - вы не должны возвращать примитивы, такие как bool или int. Вы должны возвращать объекты.
В будущем вам будет проще добавить еще одно свойство, чем поменять примитив.&lt;/p&gt;

&lt;p&gt;Допустим была у вас функция, которая возвращала True или False. Но тут, вам понадобилось третье состояние!
А давайте быстро заменим на циферки, будет возвращать 0, 1, -1. Почему бы и нет, можем же!&lt;/p&gt;

&lt;p&gt;Вы спешно поменяли, сделали рефактор силами idea или сами полазили по коду, нашли все использования этой функции и 
поменяли обработчики. Довольные выкатили на какой-нибудь слой для теста ииии…. Все сломалось. 
Колесо кармы дало новый оборот.&lt;/p&gt;

&lt;p&gt;Если вы будете возвращать объект - вы можете изменить количество его аттрибутов, 
сделать наследника класса, формирующий объект, заоверрайдить его методы.&lt;/p&gt;

&lt;p&gt;В итоге рядом с &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateClass&lt;/code&gt; будет лежать &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateResponse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Да, это нарушит конракт. Но при этом вы сохраните обратную совместимость. Ну и это не является аксиомой.
Но &lt;strong&gt;именование должно быть унифицировано для всего проекта&lt;/strong&gt;. А лучше для всей команды.&lt;/p&gt;

&lt;p&gt;Впрочем. это тема для отдельного разговора.&lt;/p&gt;

&lt;p&gt;Вот такие мысли сегодня ночью.&lt;/p&gt;

&lt;p&gt;Надеюсь подискутируем в этот раз в комментариях.&lt;/p&gt;</content><author><name></name></author><summary type="html">Предупреждение: Данный пост пропах субъективной оценкой и холиварными суждениями.</summary></entry><entry><title type="html">Parser или NewParser - вот в чем вопрос</title><link href="https://roch1990.github.io/post/2020/07/29/parser_or_new_parser.html" rel="alternate" type="text/html" title="Parser или NewParser - вот в чем вопрос" /><published>2020-07-29T00:00:00+00:00</published><updated>2020-07-29T00:00:00+00:00</updated><id>https://roch1990.github.io/post/2020/07/29/parser_or_new_parser</id><content type="html" xml:base="https://roch1990.github.io/post/2020/07/29/parser_or_new_parser.html">&lt;p&gt;&lt;strong&gt;Предупреждение: Данный пост пропах субъективной оценкой и холиварными суждениями.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Давайте поговорим про именование классов!&lt;/p&gt;

&lt;p&gt;Делая код-ревью или просматривая чужой код я часто натыкаюсь на всевозможные классы с именами типа Parser, Decoder, Encoder.&lt;/p&gt;

&lt;p&gt;Еще хлеще, когда в таком коде множество статических методов.&lt;/p&gt;

&lt;p&gt;Как Вы думаете, насколько это валидно?&lt;/p&gt;

&lt;p&gt;Лично у меня всегда возникает вопрос - “Почему колесо не называется катало?”&lt;/p&gt;

&lt;p&gt;Хочется подискутировать на этот момент.&lt;/p&gt;

&lt;p&gt;Вот проектируете вы новый супер-сервис, который парсит входящее откуда-то сообщение, расшифровывает его и складывает в БД, например.&lt;/p&gt;

&lt;p&gt;Так, ну тут у нас будет определенно какой-то хендлер, куда сообщение это валится, из которого мы сначала прогоним это сообщение через Decoder, а затем Parser и остаток сложим в БД.&lt;/p&gt;

&lt;p&gt;Логично? Ну все, пишем код.&lt;/p&gt;

&lt;p&gt;Но не все так просто, как кажется.&lt;/p&gt;

&lt;p&gt;Может измениться тип шифрования? Может. Сегодня тебе присылают сообщение в base64, завтра с битовым сдвигом и контрольной суммой.&lt;/p&gt;

&lt;p&gt;И что будем делать в таком случае? Писать еще один метод в классе Decoder. Или создадим новый класс, например NewDecoder.&lt;/p&gt;

&lt;p&gt;Может измениться структура сообщения? Может. Сегодня тебе шлют XML, завтра Yaml. Что делаем? Конечно же YamlParser.&lt;/p&gt;

&lt;p&gt;А еще бывало такое, ну признайтесь себе, когда в какой-нибудь Parser было желание засунуть еще и валидатор и математику какую-нибудь. Бывало ведь?)&lt;/p&gt;

&lt;p&gt;В итоге такой класс начинает и парсить, и валидировать, и за кофеём бегать и штаны гладить.&lt;/p&gt;

&lt;p&gt;Вот таким образом мы заимеем кучу сущностей, не передающих намерения разработчика, но в которую, как в коробку скидывают всё подряд и засовывают под стол или в дальний шкаф.&lt;/p&gt;

&lt;p&gt;Так как же быть? &lt;strong&gt;Связывайте классы с реальными вещами и называйте классы тем, чем они являются.&lt;/strong&gt; Parser - не очень реальная сущность, а что-то абстрактное, что умеет парсить сообщение. А сообщение - это конкретный объект.&lt;/p&gt;

&lt;p&gt;Валится вам на вход какое-то сообщение - назовите класс Message, задекларируйте методы - decode_base64/encode_base64/parse_to_dict.&lt;/p&gt;

&lt;p&gt;Если сообщение XML - сделайте класс XmlMessage, в котором отнаследуетесь от Message и перезагрузите нужные методы. Может и нового добавите.&lt;/p&gt;

&lt;p&gt;Если Yaml - YamlMessage.&lt;/p&gt;

&lt;p&gt;“Но ведь ничего не изменилось? Мы также плодим кучу классов и сколько хотим методов - столько и пишем!”.&lt;/p&gt;

&lt;p&gt;Еще как изменилось. Давайте приведу пример с классом Parser:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;decoded_message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Decoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;parsed_message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;save_to_db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsed_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;А теперь приведу пример с классом Message:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XmlMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save_to_db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Какие различия мы видим?&lt;/p&gt;

&lt;p&gt;Во первых это интуитивно-понятное взаимодействие с классом.&lt;/p&gt;

&lt;p&gt;Во вторых мы инкапсулируем логику - нам не нужно знать что парсить и как парсить.&lt;/p&gt;

&lt;p&gt;А в третьих, это еще и красивей выглядит :)&lt;/p&gt;

&lt;p&gt;То есть мы говорим “Колесо - катись. Колесо - остановись.”, вместо “О силы трения качения, используя свою великую мощь - дайте мне катящееся колесо”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Давайте подискутируем про это.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Может я в корне не прав и надо плодить парсеры с декодерами, а колесо называть каталом?&lt;/p&gt;</content><author><name></name></author><summary type="html">Предупреждение: Данный пост пропах субъективной оценкой и холиварными суждениями.</summary></entry><entry><title type="html">Вам не нужно 100%-е покрытие юнит-тестами</title><link href="https://roch1990.github.io/post/2020/07/24/you_doesnt_need_100_unit_tests_coverage.html" rel="alternate" type="text/html" title="Вам не нужно 100%-е покрытие юнит-тестами" /><published>2020-07-24T00:00:00+00:00</published><updated>2020-07-24T00:00:00+00:00</updated><id>https://roch1990.github.io/post/2020/07/24/you_doesnt_need_100_unit_tests_coverage</id><content type="html" xml:base="https://roch1990.github.io/post/2020/07/24/you_doesnt_need_100_unit_tests_coverage.html">&lt;p&gt;Сразу оговорюсь, что при прочтении могут возникнуть мысли о капитанстве, наравне с полыханием пятой точки.&lt;/p&gt;

&lt;p&gt;Извините уж за это, но я не могу удержаться.&lt;/p&gt;

&lt;p&gt;Я часто встречаю позицию такого рода - “Нужно сделать покрытие 100% юнит-тестами, тогда и заживем”. Нет, не заживете.&lt;/p&gt;

&lt;p&gt;А почему? А потому что скорость изменения бизнеса не будет вам позволять покрыть код 100% юнит-тестами, если это не замороженный проект.&lt;/p&gt;

&lt;p&gt;Даже если вы соберетесь командой N человек и одним геройским рывком покроете функционал юнит-тестами, то где гарантия, что близжайшая бизнес задача не сделает негодными часть тестов?&lt;/p&gt;

&lt;p&gt;И что в итоге? Вы снова собираетесь командой N-человек и правите юнит-тесты.&lt;/p&gt;

&lt;p&gt;Также, наверняка 100% тестов не получится делать, из-за того, что вы будете уходить “в насыщение”.&lt;/p&gt;

&lt;p&gt;То есть вы будете бесконечно приближаться к числу 100, но не будете достигать его.&lt;/p&gt;

&lt;p&gt;98%, 99%, 99.9%… Всегда останется строчка кода, которую не покрыть юнит-тестами.&lt;/p&gt;

&lt;p&gt;Ну и человеческий фактор. Юнит-тесты пишутся людьми, как и код. И никто не застрахован от “подгонов тестов”.&lt;/p&gt;

&lt;p&gt;Возникает закономерный вопрос - “Как же быть?”.&lt;/p&gt;

&lt;p&gt;А быть просто - &lt;strong&gt;не покрывайте код 100% юнит-тестами&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Установите планку, например 80%&lt;/p&gt;

&lt;p&gt;“Но тогда часть функционала будет непроверена!” - скажете вы. Да, юнит-тестами она покрыта не будет.&lt;/p&gt;

&lt;p&gt;Но! Но &lt;strong&gt;есть еще множество замечательных инструментов, позволяющих установить метрики качества кода, проверить его&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Давайте по порядку:&lt;/p&gt;

&lt;h3 id=&quot;pre-commit-hooks&quot;&gt;pre-commit hooks&lt;/h3&gt;

&lt;p&gt;Прекрасная вещь, которая &lt;strong&gt;проверяет ваш код перед совершением git-коммита&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;И в случае несоответствия его каким-то правилам - отклонит коммит.&lt;/p&gt;

&lt;p&gt;А может даже и поправит ваш код. Мощь этого инструмента воистину поражает.&lt;/p&gt;

&lt;p&gt;Тут и проверка на pep8 (которым грешат многие разработчики любого уровня, кстати), проверка всяких yamlов-tomlов и т.д.&lt;/p&gt;

&lt;p&gt;А самое главное, вы можете создавать свои скрипты, для проверки кода.&lt;/p&gt;

&lt;p&gt;Например я, относительно недавно написал набор скриптов, которые проверяют python код на соответствие парадигме элегантных объектов (https://github.com/roch1990/peon).&lt;/p&gt;

&lt;p&gt;Подробней узнать о инструменте вы можете тут - https://pre-commit.com/ .&lt;/p&gt;

&lt;h3 id=&quot;mutual-тестирование&quot;&gt;Mutual тестирование&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Производится изменение вашего кода с последующей проверкой&lt;/strong&gt; на “изменилось ли что-то в выполнении кода или нет”.&lt;/p&gt;

&lt;p&gt;Например у вас есть вычисление &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return 2 + 2&lt;/code&gt;. Соответственно вы ожидаете 4 где-то далее в вашем ПО.&lt;/p&gt;

&lt;p&gt;Тут заходит злобный Mutual-демон и меняет этот кусок кода на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return 2-2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Все, выполнение программы ломается. Или нет?&lt;/p&gt;

&lt;p&gt;Если не сломается, то &lt;strong&gt;возможно что-то у вас в коде сделано не так&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Из минусов такого тестирования могу лишь указать на затраты на производительность - мне удалось положить гитлаб раннер запускам этих тестов.&lt;/p&gt;

&lt;p&gt;Проверить это вы можете (не ухайдакивание раннера :), например воспользуясь библиотекой mutmut для python3 (https://pypi.org/project/mutmut/)&lt;/p&gt;

&lt;h3 id=&quot;интеграционное-тестирование&quot;&gt;Интеграционное тестирование&lt;/h3&gt;

&lt;p&gt;Казалось бы - “Да ладно, мы все знаем про интеграционное тестирование!”. Но все ли его делают? И делают ли правильно?&lt;/p&gt;

&lt;p&gt;В интеграционном тестировании вы не должны проверять просто тыкая метод палочкой “Ой, отправлю запрос, что-то прилетело в ответ - отлично”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Постарайтесь охватить большинство бизнес-процессов&lt;/strong&gt;. Верные ответы, ошибки. Пусть у вас не будет весь возможный перебор входных параметров запросов (вдруг у вас есть какие-то опциональные).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Не стремайтесь писать отдельное ПО&lt;/strong&gt; для проведения интеграционного тестирования, вместо написания отдельных модулей, чтобы они содержались в вашем ПО “из-коробки”.&lt;/p&gt;

&lt;p&gt;Пусть это будет bash-скрипт/perl-скрипт, хоть ПО на С++. Главное, чтобы проверка производилась (ну и не вставали волосы дыбом у команды от выбранной реализации).&lt;/p&gt;

&lt;h3 id=&quot;security-тесты&quot;&gt;Security тесты&lt;/h3&gt;

&lt;p&gt;Это очень редкий зверь в современном мире, что очень зря. &lt;strong&gt;Сколько сейчас новостей о утечках информации из-за обычных простых ошибок в коде&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Посмотрите любую ленту новостей, за ближайшие неделю-месяц вы обязательно найдете такую новость.&lt;/p&gt;

&lt;p&gt;Чтобы избежать этого - добрые люди придумали &lt;strong&gt;программные пакеты, которые проверяют ваш код на common security issues&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Удобно, не правда ли? &lt;strong&gt;Добавляете одну строчку в вашу сборку и смотрите отчет&lt;/strong&gt;, никаких лишних телодвижений.&lt;/p&gt;

&lt;p&gt;И в будущем у вас будет меньше проблем с безопасностью.&lt;/p&gt;

&lt;p&gt;Проверить свой код на python можно, используя библиотеку &lt;a href=&quot;https://pypi.org/project/bandit/&quot;&gt;bandit&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;статический-анализ&quot;&gt;Статический анализ&lt;/h3&gt;

&lt;p&gt;В современном мире это уже чуть ли не обязательный инструмент, который нужно использовать для вашего кода.&lt;/p&gt;

&lt;p&gt;И &lt;strong&gt;технический долг посчитает, и надежность кода, и укажет на уязвимости&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Вообщем выбираете себе подходящий инструмент (благо их сейчас вагон и маленькая тележка, например &lt;a href=&quot;https://www.sonarqube.org/&quot;&gt;sonarqube&lt;/a&gt; и вперед за приключениями.&lt;/p&gt;

&lt;p&gt;Таким образом, кроме одного этапа тестирования, у вас будет, как минимум 5.&lt;/p&gt;

&lt;p&gt;Пусть &lt;strong&gt;это не покроет весь функционал&lt;/strong&gt; (останется какая-то гадость, наверняка), но &lt;strong&gt;вероятность достать белый шарик из коробки, с 5 попыток гораздо выше, чем с 1й попытки&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;В данной статье я целенаправленно не затронул такие вещи как:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;performance testing&lt;/li&gt;
  &lt;li&gt;ci/cd&lt;/li&gt;
  &lt;li&gt;документация&lt;/li&gt;
  &lt;li&gt;мониторинг&lt;/li&gt;
  &lt;li&gt;code review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Хотя они тоже привносят свой посильный вклад в качество кода.&lt;/p&gt;

&lt;p&gt;Но это тема для отдельного разговора.&lt;/p&gt;

&lt;p&gt;Данная статья не претендует на научность (пока), но её цель - чтобы хотя-бы у одного разработчика что-то зашевелилось в голове и он решил попробовать разнообразить проверки своего кода.&lt;/p&gt;

&lt;p&gt;Если тема понравилась и интересно почитать что-то подобное еще, либо раскрыть что-то поподробнее - пишите комментарии, ставьте лайк.&lt;/p&gt;

&lt;p&gt;Всем мир и огнетушителей. Я не тролль :)&lt;/p&gt;

&lt;p&gt;P.S.: Ах да, забыл привести вам моё &lt;a href=&quot;https://youtu.be/99mU1gSLbkU&quot;&gt;вдохновение&lt;/a&gt;.&lt;/p&gt;</content><author><name></name></author><summary type="html">Сразу оговорюсь, что при прочтении могут возникнуть мысли о капитанстве, наравне с полыханием пятой точки.</summary></entry></feed>