HTML Diff
1 added 1 removed
Original 2026-01-01
Modified 2026-02-26
1 <p><strong>Рассказываем, как начинающему разработчику проводить модульное тестирование вашего Python-приложения, и с помощью чего обеспечить и проверить сообщения о фиксации в Git.</strong></p>
1 <p><strong>Рассказываем, как начинающему разработчику проводить модульное тестирование вашего Python-приложения, и с помощью чего обеспечить и проверить сообщения о фиксации в Git.</strong></p>
2 <p><em>Эта<a>вторая</a>статья<a>из цикла</a>, в котором рассказывается о лучших практиках современного Python. В этом цикле статей все примеры основаны на реализации простого проекта, который представляет собой функцию Python, которая суммирует данные, присутствующие в<a>pandas DataFrame</a>. Функция выводит количество строк и столбцов и частоту каждого типа данных, присутствующих в pandas DataFrame.</em></p>
2 <p><em>Эта<a>вторая</a>статья<a>из цикла</a>, в котором рассказывается о лучших практиках современного Python. В этом цикле статей все примеры основаны на реализации простого проекта, который представляет собой функцию Python, которая суммирует данные, присутствующие в<a>pandas DataFrame</a>. Функция выводит количество строк и столбцов и частоту каждого типа данных, присутствующих в pandas DataFrame.</em></p>
3 <p>Хорошая практика разработки программного обеспечения всегда приносит много долгосрочных выгод. Например, написание модульных тестов позволяет поддерживать большие кодовые базы и гарантирует, что определенный фрагмент вашего кода ведет себя так, как ожидается. Написание последовательных коммитов в Git также улучшает сотрудничество между участниками проекта. Хорошо составленные сообщения коммитов Git открывают путь к автоматическому версионированию и автоматически генерируемым файлам журнала изменений. Поэтому сейчас в сообществе разработчиков предпринимается множество попыток нормализовать сообщения, написанные в наших Git-коммитах.</p>
3 <p>Хорошая практика разработки программного обеспечения всегда приносит много долгосрочных выгод. Например, написание модульных тестов позволяет поддерживать большие кодовые базы и гарантирует, что определенный фрагмент вашего кода ведет себя так, как ожидается. Написание последовательных коммитов в Git также улучшает сотрудничество между участниками проекта. Хорошо составленные сообщения коммитов Git открывают путь к автоматическому версионированию и автоматически генерируемым файлам журнала изменений. Поэтому сейчас в сообществе разработчиков предпринимается множество попыток нормализовать сообщения, написанные в наших Git-коммитах.</p>
4 <p><a>В первой части</a>этой серии мы настраивали наш проект, устанавливая различные версии Python с помощью pyenv, устанавливая локальную версию Python с помощью pyenv, инкапсулируя его в виртуальную среду с помощью poetry. Здесь мы покажем более подробно, как проводить модульное тестирование вашего Python-приложения и как обеспечить и проверить сообщения о коммитах в Git. Исходный код, связанный с этой статьей, опубликован на<a>GitHub</a>.</p>
4 <p><a>В первой части</a>этой серии мы настраивали наш проект, устанавливая различные версии Python с помощью pyenv, устанавливая локальную версию Python с помощью pyenv, инкапсулируя его в виртуальную среду с помощью poetry. Здесь мы покажем более подробно, как проводить модульное тестирование вашего Python-приложения и как обеспечить и проверить сообщения о коммитах в Git. Исходный код, связанный с этой статьей, опубликован на<a>GitHub</a>.</p>
5 <h2>Содержание</h2>
5 <h2>Содержание</h2>
6 <ul><li><a>Тестирование кода</a></li>
6 <ul><li><a>Тестирование кода</a></li>
7 <li><a>Применение правил по созданию сообщений Git-коммитов в проекте Python</a></li>
7 <li><a>Применение правил по созданию сообщений Git-коммитов в проекте Python</a></li>
8 <li><a>Заключение</a></li>
8 <li><a>Заключение</a></li>
9 <li><a>Краткая памятка</a></li>
9 <li><a>Краткая памятка</a></li>
10 </ul><h2>Тестирование кода</h2>
10 </ul><h2>Тестирование кода</h2>
11 <p>Перейдите в корневой каталог вашего проекта и активируйте виртуальное окружение:</p>
11 <p>Перейдите в корневой каталог вашего проекта и активируйте виртуальное окружение:</p>
12 <p><em>Примечание переводчика: Поскольку со времени написания оригинальной статьи требования numpy к версии Python изменились, перед добавлением зависимостей необходимо внести изменения в файл pyproject.toml:</em></p>
12 <p><em>Примечание переводчика: Поскольку со времени написания оригинальной статьи требования numpy к версии Python изменились, перед добавлением зависимостей необходимо внести изменения в файл pyproject.toml:</em></p>
13 <p>Добавим несколько зависимостей с помощью poetry:</p>
13 <p>Добавим несколько зависимостей с помощью poetry:</p>
14 <p>Флаг -D указывает, что зависимость применяется только к среде разработки.</p>
14 <p>Флаг -D указывает, что зависимость применяется только к среде разработки.</p>
15 <p>Исходя из ожидаемого результата работы проекта, наша программа состоит из трех шагов:</p>
15 <p>Исходя из ожидаемого результата работы проекта, наша программа состоит из трех шагов:</p>
16 <ol><li>Получение формы pandas DataFrame</li>
16 <ol><li>Получение формы pandas DataFrame</li>
17 <li>Получение частоты типов pandas dtypes.</li>
17 <li>Получение частоты типов pandas dtypes.</li>
18 <li>Объединение этих двух результатов в единый DataFrame, который будет использован для вывода окончательного результата.</li>
18 <li>Объединение этих двух результатов в единый DataFrame, который будет использован для вывода окончательного результата.</li>
19 - </ol><p>После получения окончательного DataFrame выводится результат, как показано выше. В связи с этим наш код может выглядеть следующим образом:</p>
19 + </ol><p>После получения окончательного DataFrame выводтся результат, как показано выше. В связи с этим наш код может выглядеть следующим образом:</p>
20 <p>Теперь начнем писать модульные тесты. Мы будем использовать инструмент unittest, доступный в стандартной библиотеке Python. Возможно вы помните, что в<a>предыдущей</a>статье<a>pytest</a>был определен как зависимость для тестирования. Это не проблема, потому что pytest нативно запускает тесты, написанные с помощью библиотеки unittest.</p>
20 <p>Теперь начнем писать модульные тесты. Мы будем использовать инструмент unittest, доступный в стандартной библиотеке Python. Возможно вы помните, что в<a>предыдущей</a>статье<a>pytest</a>был определен как зависимость для тестирования. Это не проблема, потому что pytest нативно запускает тесты, написанные с помощью библиотеки unittest.</p>
21 <p>Юнит-тесты - это методы, которые, как ожидает unittest, будут описаны внутри классов Python. Выберите для своих тестовых классов и методов описывающее имя - оно должно начинаться с test_. Дополнительно unittest использует ряд специальных тестовых методов, унаследованных от класса unittest.TestCase.</p>
21 <p>Юнит-тесты - это методы, которые, как ожидает unittest, будут описаны внутри классов Python. Выберите для своих тестовых классов и методов описывающее имя - оно должно начинаться с test_. Дополнительно unittest использует ряд специальных тестовых методов, унаследованных от класса unittest.TestCase.</p>
22 <p><strong>На практике тест должен:</strong></p>
22 <p><strong>На практике тест должен:</strong></p>
23 <ul><li>Охватывать одну функцию</li>
23 <ul><li>Охватывать одну функцию</li>
24 <li>Быть автономным</li>
24 <li>Быть автономным</li>
25 <li>Не требовать внешних инструкций</li>
25 <li>Не требовать внешних инструкций</li>
26 <li>Воссоздавать условия достижения результата.</li>
26 <li>Воссоздавать условия достижения результата.</li>
27 </ul><p>Чтобы воссоздать необходимую рабочую среду, необходимо написать код настройки. Если этот код окажется избыточным, реализуйте метод setUp(), который будет выполняться перед каждым тестом. Это очень удобно для повторного использования и реорганизации кода. В зависимости от сценария использования, возможно придется выполнять регулярные операции после выполнения тестов. Для этого вы можете использовать метод tearDown().</p>
27 </ul><p>Чтобы воссоздать необходимую рабочую среду, необходимо написать код настройки. Если этот код окажется избыточным, реализуйте метод setUp(), который будет выполняться перед каждым тестом. Это очень удобно для повторного использования и реорганизации кода. В зависимости от сценария использования, возможно придется выполнять регулярные операции после выполнения тестов. Для этого вы можете использовать метод tearDown().</p>
28 <p>Сначала вы можете посмотреть unit-тест, который мы реализовали для функции data_summary():</p>
28 <p>Сначала вы можете посмотреть unit-тест, который мы реализовали для функции data_summary():</p>
29 <p>Метод setUp() инициализирует два разных pandas DataFrame. self.exp_df - это результирующий DataFrame, который мы ожидаем получить после вызова функции data_summary(), а self.df - это DataFrame, который используется для тестирования наших функций. Сейчас ожидается, что тесты окажутся неудачными, потому что логика не была реализована. Для тестирования с помощью poetry используйте команду:</p>
29 <p>Метод setUp() инициализирует два разных pandas DataFrame. self.exp_df - это результирующий DataFrame, который мы ожидаем получить после вызова функции data_summary(), а self.df - это DataFrame, который используется для тестирования наших функций. Сейчас ожидается, что тесты окажутся неудачными, потому что логика не была реализована. Для тестирования с помощью poetry используйте команду:</p>
30 <p>Использование флага -v позволяет получить более подробный вывод результатов тестирования. Вы можете видеть, что тесты помечены в соответствии с именами классов и функций, которые вы задали. В нашем случае это ::TestDataSummary::test_data_summary.</p>
30 <p>Использование флага -v позволяет получить более подробный вывод результатов тестирования. Вы можете видеть, что тесты помечены в соответствии с именами классов и функций, которые вы задали. В нашем случае это ::TestDataSummary::test_data_summary.</p>
31 <p>Поменяем код для соответствия unit-тестам:</p>
31 <p>Поменяем код для соответствия unit-тестам:</p>
32 <p>Снова запустим тесты:</p>
32 <p>Снова запустим тесты:</p>
33 <p>И последнее. В наших тестах мы не проверяли фактический вывод. Наш модуль предназначен для вывода строкового представления сводки DataFrame. Существуют решения для достижения этой цели с помощью unittest, но мы будем использовать pytest для этого теста. Удивительно, не правда ли? Как уже говорилось, pytest очень хорошо работает с unittest, и сейчас мы это проиллюстрируем. Вот код для этого теста:</p>
33 <p>И последнее. В наших тестах мы не проверяли фактический вывод. Наш модуль предназначен для вывода строкового представления сводки DataFrame. Существуют решения для достижения этой цели с помощью unittest, но мы будем использовать pytest для этого теста. Удивительно, не правда ли? Как уже говорилось, pytest очень хорошо работает с unittest, и сейчас мы это проиллюстрируем. Вот код для этого теста:</p>
34 <p>Обратите внимание на декоратор @pytest.fixture(autouse=True) и функцию, которую он оборачивает (_pass_fixture). В терминологии модульного тестирования этот метод называется фикстурой (<a>fixture</a>). Фикстуры - это функции (или методы, если Вы используете подход ООП), которые будут выполняться перед каждым тестом, к которому они применяются. Фикстуры используются для передачи данных в тесты. Они выполняют ту же задачу, что и метод setUp(), который мы использовали ранее. Здесь мы используем заранее определенное фикстуру под названием capsys для захвата стандартного вывода (stdout) и повторного использования его в нашем тесте. Теперь изменим соответствующим образом функцию display_summary():</p>
34 <p>Обратите внимание на декоратор @pytest.fixture(autouse=True) и функцию, которую он оборачивает (_pass_fixture). В терминологии модульного тестирования этот метод называется фикстурой (<a>fixture</a>). Фикстуры - это функции (или методы, если Вы используете подход ООП), которые будут выполняться перед каждым тестом, к которому они применяются. Фикстуры используются для передачи данных в тесты. Они выполняют ту же задачу, что и метод setUp(), который мы использовали ранее. Здесь мы используем заранее определенное фикстуру под названием capsys для захвата стандартного вывода (stdout) и повторного использования его в нашем тесте. Теперь изменим соответствующим образом функцию display_summary():</p>
35 <p>Ещё раз запустим тесты:</p>
35 <p>Ещё раз запустим тесты:</p>
36 <p>Тесты прошли успешно. Пришло время зафиксировать нашу работу и поделиться ею, например, опубликовав на GitHub. Перед этим давайте подробно рассмотрим, как правильно сообщать о нашей работе с помощью сообщений о коммитах Git, соблюдая и поддерживая единый стандарт.</p>
36 <p>Тесты прошли успешно. Пришло время зафиксировать нашу работу и поделиться ею, например, опубликовав на GitHub. Перед этим давайте подробно рассмотрим, как правильно сообщать о нашей работе с помощью сообщений о коммитах Git, соблюдая и поддерживая единый стандарт.</p>
37 <blockquote><h3>Читайте другие статьи в блоге:</h3>
37 <blockquote><h3>Читайте другие статьи в блоге:</h3>
38 <p>Как принять участие в работе Open Source проектов на GitHub.<a>Краткое руководство для начинающих</a></p>
38 <p>Как принять участие в работе Open Source проектов на GitHub.<a>Краткое руководство для начинающих</a></p>
39 </blockquote><h2>Применение правил по созданию сообщений Git-коммитов в проекте Python</h2>
39 </blockquote><h2>Применение правил по созданию сообщений Git-коммитов в проекте Python</h2>
40 <p>Написание оптимальных Git-коммит сообщений - непростая задача. Сообщения должны быть четкими, читаемыми и понятными в долгосрочной перспективе. Спецификация<a>Conventional Commits</a>предлагает набор правил для создания однозначной истории коммитов. На Хекслете есть<a>большая статья</a>, посвященная правильному именованию коммитов.</p>
40 <p>Написание оптимальных Git-коммит сообщений - непростая задача. Сообщения должны быть четкими, читаемыми и понятными в долгосрочной перспективе. Спецификация<a>Conventional Commits</a>предлагает набор правил для создания однозначной истории коммитов. На Хекслете есть<a>большая статья</a>, посвященная правильному именованию коммитов.</p>
41 <h3>Использование commitizen</h3>
41 <h3>Использование commitizen</h3>
42 <p>Мы будем использовать пакет<a>commitizen</a>для интеграции Conventional Commits в наш проект на Python. Добавим этот пакет в зависимости разработчика:</p>
42 <p>Мы будем использовать пакет<a>commitizen</a>для интеграции Conventional Commits в наш проект на Python. Добавим этот пакет в зависимости разработчика:</p>
43 <p>Чтобы настроить commitizen для своего проекта, выполните команду cz init. Она предложит нам ответить на ряд вопросов:</p>
43 <p>Чтобы настроить commitizen для своего проекта, выполните команду cz init. Она предложит нам ответить на ряд вопросов:</p>
44 <p>Выберем здесь все варианты по умолчанию, так как они полностью соответствуют нашей реальной ситуации. Последний вопрос спрашивает нас, нужно ли использовать хук<a>pre-commit</a>. Мы собираемся вернуться к этому позже. Поэтому пока просто ответим "нет"(n). Если мы посмотрим на файл pyproject.toml, то увидим, что была добавлена новая запись под названием [tool.commitizen]:</p>
44 <p>Выберем здесь все варианты по умолчанию, так как они полностью соответствуют нашей реальной ситуации. Последний вопрос спрашивает нас, нужно ли использовать хук<a>pre-commit</a>. Мы собираемся вернуться к этому позже. Поэтому пока просто ответим "нет"(n). Если мы посмотрим на файл pyproject.toml, то увидим, что была добавлена новая запись под названием [tool.commitizen]:</p>
45 <p>Проверить коммит-сообщение, можно при помощи следующей команды:</p>
45 <p>Проверить коммит-сообщение, можно при помощи следующей команды:</p>
46 <p>Наше сообщение отклонено, потому что оно не соответствует выбранным правилам для коммит-сообщений. Последняя строка предлагает некоторые шаблоны для использования. Уделите немного времени чтению документации о<a>соглашении о коммитах</a>и выполните команду cz info, чтобы распечатать краткую документацию:</p>
46 <p>Наше сообщение отклонено, потому что оно не соответствует выбранным правилам для коммит-сообщений. Последняя строка предлагает некоторые шаблоны для использования. Уделите немного времени чтению документации о<a>соглашении о коммитах</a>и выполните команду cz info, чтобы распечатать краткую документацию:</p>
47 <p>Эта команда подскажет вам, как написать сообщение о коммите. Здесь формат должен быть таким: "[тип]: [СООБЩЕНИЕ]". Для нас это выглядит так:</p>
47 <p>Эта команда подскажет вам, как написать сообщение о коммите. Здесь формат должен быть таким: "[тип]: [СООБЩЕНИЕ]". Для нас это выглядит так:</p>
48 <p>Очень хорошо, наше коммит-сообщение считается корректным. Но подождите. Проверять коммит-сообщения каждый раз с помощью commitizen может быть утомительно, и это не даёт гарантии, что коммит будет принят. Было бы лучше автоматически проверять сообщение каждый раз, когда мы используем команду git commit. Именно в этом случае в действие вступает pre-commit хук.</p>
48 <p>Очень хорошо, наше коммит-сообщение считается корректным. Но подождите. Проверять коммит-сообщения каждый раз с помощью commitizen может быть утомительно, и это не даёт гарантии, что коммит будет принят. Было бы лучше автоматически проверять сообщение каждый раз, когда мы используем команду git commit. Именно в этом случае в действие вступает pre-commit хук.</p>
49 <h3>Автоматическое соблюдение соглашений о Git-коммитах при помощи pre-commit</h3>
49 <h3>Автоматическое соблюдение соглашений о Git-коммитах при помощи pre-commit</h3>
50 <p>Хуки Git полезны для автоматизации и выполнения некоторых действий на разных этапах жизненного цикла Git. Хук pre-commit позволяет запускать скрипты до того, как будет выполнен Git-коммит. Мы можем использовать хук для проверки сообщений о коммитах и предотвращения использования Git сообщения, которое не соответствует нашим ожиданиям. Хук активен как из командной строки, так и из любых инструментов, взаимодействующих с репозиторием Git, в котором зарегистрирован хук, включая вашу любимую IDE.</p>
50 <p>Хуки Git полезны для автоматизации и выполнения некоторых действий на разных этапах жизненного цикла Git. Хук pre-commit позволяет запускать скрипты до того, как будет выполнен Git-коммит. Мы можем использовать хук для проверки сообщений о коммитах и предотвращения использования Git сообщения, которое не соответствует нашим ожиданиям. Хук активен как из командной строки, так и из любых инструментов, взаимодействующих с репозиторием Git, в котором зарегистрирован хук, включая вашу любимую IDE.</p>
51 <p><a>pre-commit</a>- это фреймворк для управления и поддержки многоязычных хуков pre-commit. Если вы хотите узнать больше о внутреннем устройстве и спектре возможностей pre-commit, то можете прочитать<a>документацию</a>по его использованию.</p>
51 <p><a>pre-commit</a>- это фреймворк для управления и поддержки многоязычных хуков pre-commit. Если вы хотите узнать больше о внутреннем устройстве и спектре возможностей pre-commit, то можете прочитать<a>документацию</a>по его использованию.</p>
52 <p>Чтобы установить pre-commit, просто выполните команду:</p>
52 <p>Чтобы установить pre-commit, просто выполните команду:</p>
53 <p>Для автоматизации проверки коммита Git нам сначала нужно создать конфигурационный файл .pre-commit-config.yaml:</p>
53 <p>Для автоматизации проверки коммита Git нам сначала нужно создать конфигурационный файл .pre-commit-config.yaml:</p>
54 <p>Далее мы можем установить хук с источником, определенным в параметре repo:</p>
54 <p>Далее мы можем установить хук с источником, определенным в параметре repo:</p>
55 <p>Теперь, когда все готово, мы можем использовать наш Git-хук:</p>
55 <p>Теперь, когда все готово, мы можем использовать наш Git-хук:</p>
56 <p>pre-commit устанавливает среду для выполнения своих проверок. Как вы можете видеть здесь, сообщение о коммите прошло проверку. В завершение мы можем закоммитить изменения, внесенные в файлы сборки (poetry.lock, pyproject.toml) и наш модуль:</p>
56 <p>pre-commit устанавливает среду для выполнения своих проверок. Как вы можете видеть здесь, сообщение о коммите прошло проверку. В завершение мы можем закоммитить изменения, внесенные в файлы сборки (poetry.lock, pyproject.toml) и наш модуль:</p>
57 <p>Теперь мы можем отправить все в наш репозиторий GitHub:</p>
57 <p>Теперь мы можем отправить все в наш репозиторий GitHub:</p>
58 <h2>Заключение</h2>
58 <h2>Заключение</h2>
59 <p>Мы рассмотрели несколько тем:</p>
59 <p>Мы рассмотрели несколько тем:</p>
60 <ul><li><p>На первом этапе мы узнали, как писать модульные тесты для вашего кода. Писать тесты до кода поможет вам уточнить API и ожидаемый результат до реализации в коде. Мы использовали unittest, который уже доступен в стандартной библиотеке Python. Также было продемонстрировано использование библиотеки pytest для написания тестов. Очень удобно то, что pytest с самого начала поддерживает класс unittest.TestCase. Вы можете писать свои тесты с помощью любой из двух библиотек или даже смешивать их в зависимости от ваших потребностей и иметь одну общую команду для запуска всех тестов.</p>
60 <ul><li><p>На первом этапе мы узнали, как писать модульные тесты для вашего кода. Писать тесты до кода поможет вам уточнить API и ожидаемый результат до реализации в коде. Мы использовали unittest, который уже доступен в стандартной библиотеке Python. Также было продемонстрировано использование библиотеки pytest для написания тестов. Очень удобно то, что pytest с самого начала поддерживает класс unittest.TestCase. Вы можете писать свои тесты с помощью любой из двух библиотек или даже смешивать их в зависимости от ваших потребностей и иметь одну общую команду для запуска всех тестов.</p>
61 </li>
61 </li>
62 <li><p>Мы рассмотрели, как обеспечить соблюдение хороших практик при написании сообщений о коммитах в Git. Предлагаемое нами решение основано на использовании двух пакетов Python:<a>commitizen</a>и<a>pre-commit</a>. Первый предоставляет инструменты для проверки соответствия сообщения выбранным вами соглашениям. Второй автоматизирует процесс с помощью Git-хука.</p>
62 <li><p>Мы рассмотрели, как обеспечить соблюдение хороших практик при написании сообщений о коммитах в Git. Предлагаемое нами решение основано на использовании двух пакетов Python:<a>commitizen</a>и<a>pre-commit</a>. Первый предоставляет инструменты для проверки соответствия сообщения выбранным вами соглашениям. Второй автоматизирует процесс с помощью Git-хука.</p>
63 </li>
63 </li>
64 </ul><h2>Краткая памятка</h2>
64 </ul><h2>Краткая памятка</h2>
65 <h3>poetry</h3>
65 <h3>poetry</h3>
66 <ul><li><p>Добавьте зависимости проекта:</p>
66 <ul><li><p>Добавьте зависимости проекта:</p>
67 `poetry add [package_name]`</li>
67 `poetry add [package_name]`</li>
68 <li><p>Добавьте зависимости для разработки:</p>
68 <li><p>Добавьте зависимости для разработки:</p>
69 `poetry add -D [package_name]`</li>
69 `poetry add -D [package_name]`</li>
70 <li><p>Запуск тестов:</p>
70 <li><p>Запуск тестов:</p>
71 ` poetry run pytest`</li>
71 ` poetry run pytest`</li>
72 </ul><h3>commitizen</h3>
72 </ul><h3>commitizen</h3>
73 <ul><li><p>Инициализация commitizen:</p>
73 <ul><li><p>Инициализация commitizen:</p>
74 `cz init`</li>
74 `cz init`</li>
75 <li><p>Проверка коммита:</p>
75 <li><p>Проверка коммита:</p>
76 ` cz check -m "YOUR MESSAGE"`</li>
76 ` cz check -m "YOUR MESSAGE"`</li>
77 </ul><h3>pre-commit</h3>
77 </ul><h3>pre-commit</h3>
78 <ul><li><p>Создание файла конфигурации по умолчанию:</p>
78 <ul><li><p>Создание файла конфигурации по умолчанию:</p>
79 `pre-commit sample-config`</li>
79 `pre-commit sample-config`</li>
80 <li><p>Установить git-хук:</p>
80 <li><p>Установить git-хук:</p>
81 `pre-commit install --hook-type [hook_name]`</li>
81 `pre-commit install --hook-type [hook_name]`</li>
82 </ul>
82 </ul>