текст перекладу Аналізатор файлової системи на Python за допомогою TDD крок за кроком (Частина 1)

текст перекладу
Нещодавно мені було доручено створити інструмент командного рядка для аналізу заданої директорії. Як прихильник TDD (розробка через тестування), я дуже зрадів, адже це була чудова можливість застосувати мій улюблений підхід. Справа в тому, що в повсякденній роботі рідко трапляються можливості для такого підходу. Тож, якщо вам це цікаво, приєднуйтесь, давайте разом крок за кроком.

Спочатку дозвольте описати інструмент, який ми будемо впроваджувати. Це інструмент, що має мінімалістичний характер, призначений для рекурсивного глибокого читання директорії та виведення трьох рядків даних: розміри, категоризовані за типами файлів, список тих, що мають незвичні дозволи, і ще один список для великих файлів. Наскільки великими? Ну, давайте зробимо це налаштовуваним. Параметр порогу ми передаватимемо ззовні.

По-друге, що таке TDD? Згідно з Wiki:

Розробка через тестування (TDD) — це спосіб написання коду, який передбачає написання автоматизованого тесту для окремих одиничних функцій, який спочатку не проходить, потім написання стільки коду, щоб тест пройшов, потім рефакторинг тестового та виробничого коду, і знову повторювати цей цикл для нового тесту.

pic

А чому TDD? Безумовно, у нього є багато переваг, що стосуються якості коду та налагодження. Для мене ж серед цих та інших чудових особливостей головним аргументом є відсутність потреби у списку завдань. Ви фактично маєте і план, і тести одночасно, а ще бонусом — не забуваєте про перевірку крайніх випадків.

І я готовий поспорити, що коли нас просять написати одиничні тести та залишити це на кінець, частіше, ніж нам хочеться визнати, ми поспішаємо, пропускаємо кілька крайніх випадків і пишемо більш узагальнені "дивись, воно працює успішно" і "дивись, воно правильно падає" тести. Що не завжди є хорошою ідеєю. З TDD, з іншого боку, ви встановлюєте свої очікування з самого початку, визначаєте, як має працювати код, а потім реалізуєте цю поведінку, замість того, щоб перевіряти/тестувати, як працює готовий додаток.

Тепер достатньо балакати, код сам не напишеться.

Давайте створимо проектну директорію з такою структурою:

├── file_system_analyzer  
│ ├── __main__.py  
│ ├── analyzer  
│ │ ├── __init__.py  
│ │ ├── large_files.py  
│ │ ├── permissions.py  
│ │ └── size_analysis.py  
│ └── setup.py  
└── tests  
 ├── test_large_files.py  
 ├── test_permissions.py  
 └── test_size_analysis.py

Я пропоную залишити __main__.py і setup.py на кінець, коли ми будемо працювати над пакетом, а почати з фільтрації великих файлів. Давайте реалізуємо порожню функцію, достатню для того, щоб тест міг коректно імпортуватися і запуститися.

# file_system_analyzer/analyzer/large_files.py  
def find_large_files(directory, size_threshold):  
 pass

Тепер давайте подумаємо, як повинна поводитися наша функція find_large_files. Спочатку ми, ймовірно, хочемо, щоб вона повертала шлях до файлів у заданій директорії, які більші за заданий поріг (1кб у цьому прикладі).

# tests/test_large_files.py  
import pytest  
from file_system_analyzer.analyzer.large_files import find_large_files  

def test_find_large_files():  
 result = find_large_files('/path/to/directory', 1000000)  
 assert result == ['/path/to/file2.jpg']

Тепер, коли ми запустимо pytest з кореневої директорії проекту, він повинен вивести таку помилку:

FAILED tests/test_large_files.py::test_find_large_files - AssertionError: assert None == ['/path/to/file2.jpg']

Це тому, що ми очікуємо список шляхів до файлів (що цілком виправдано), а функція повертає None завдяки pass.

Примітка: щоб скоротити детальний вивід для більш чіткого перегляду в терміналі, ви можете використовувати опцію --tb=short:

pytest --tb=short

Тепер, невеликий тест, я не люблю отримувати загальні повідомлення про помилки, коли не знаю, що саме пішло не так.
текст перекладу
Отже, для того щоб наш інструмент викидав відповідні помилки, коли передається некоректна директорія або поріг, я пропоную створити власний тип помилки FileAnalyzerError, а потім розширити зазначені помилки на його основі:

# file_system_analyzer/analyzer/errors.py  
class FileAnalyzerError(Exception):  
 pass  

class DirectoryNotFoundError(FileAnalyzerError):  
 pass  

class InvalidThresholdError(FileAnalyzerError):  
 pass

Тепер давайте імпортуємо наші помилки в тестовий файл і реалізуємо тести (+ встановимо очікування щодо того, які помилки повинні виникати для кожного випадку).

# tests/test_large_files.py  
from file_system_analyzer.analyzer.errors import DirectoryNotFoundError, InvalidThresholdError  

def test_find_large_files_directory_not_found():  
 with pytest.raises(DirectoryNotFoundError):  
 find_large_files('/invalid/directory', 1000000)  

def test_find_large_files_invalid_threshold():  
 with pytest.raises(InvalidThresholdError):  
 find_large_files('/path/to/directory', -100)

На цьому етапі, при запуску тестів, ми повинні побачити більше помилок (наче однієї було недостатньо):

============================================================================ short test summary info =============================================================================  
FAILED tests/test_large_files.py::test_find_large_files - AssertionError: assert None == ['/path/to/file2.jpg']  
FAILED tests/test_large_files.py::test_find_large_files_directory_not_found - Failed: DID NOT RAISE   
FAILED tests/test_large_files.py::test_find_large_files_invalid_threshold - Failed: DID NOT RAISE   
=============================================================================== 3 failed in 0.15s ================================================================================

🥳 Тепер обійміть помилку, саме це ми й очікували. Це означає, що ми маємо правильну базову структуру для нашого проекту, встановили початкові очікування від коду та маємо план, як рухатися далі. І саме це ми й зробимо в Частині 2.

Перекладено з: File System Analyzer on Python with TDD step by step (Part 1)

Leave a Reply

Your email address will not be published. Required fields are marked *