Це передостанній етап нашого посібника та реалізація інструменту CMD, хоча останній в циклі тестування-код-тест. Сподіваюсь, на цей момент ви вже отримали уявлення про TDD з Частини 1 та Частини 2 (і, сподіваюся, вам це сподобалось), і тепер ви знаєте, чого очікувати. Далі, використовуючи набуті навички, ми застосуємо ті ж самі техніки та підхід, але цього разу для інших функцій нашого додатку. Наприклад, для обчислення розмірів файлів та фільтрації за типом файлів.
І ви правильно здогадались, спочатку ми пишемо нашу функцію calculate_size
, щоб вона просто існувала і не повертала нічого:
# file_system_analyzer/analyzer/size_analysis.py
def calculate_size(directory):
pass
Знову ж таки, набір тестів служить визначенням модуля і наших чітких очікувань від нього, як для успішного випадку, так і для випадку помилки валідації.
# tests/test_size_analysis.py
import pytest
from file_system_analyzer.analyzer.size_analysis import calculate_size
from file_system_analyzer.analyzer.errors import DirectoryNotFoundError
def test_calculate_size():
result = calculate_size('/path/to/directory')
assert result == {'text': 100, 'image': 200}
def test_calculate_size_directory_not_found():
with pytest.raises(DirectoryNotFoundError):
calculate_size('/invalid/directory')
Очевидно, що зараз обидва тести не проходять. Наша задача — створити модуль так, щоб він відповідав вимогам.
Щоб заощадити час і не набридати вам, я надам фінальну реалізацію функції:
# file_system_analyzer/analyzer/size_analysis.py
import os
from collections import defaultdict
from analyzer.traversal import traverse_directory
from analyzer.file_categorization import categorize_file
from analyzer.errors import DirectoryNotFoundError
def calculate_size(directory):
if not os.path.isdir(directory):
raise DirectoryNotFoundError(
f"The specified directory '{directory}' does not exist or is not a directory."
)
sizes = defaultdict(int)
for file_path in traverse_directory(directory):
category = categorize_file(file_path)
sizes[category] += os.path.getsize(file_path)
return dict(sizes)
Тепер рефакторимо тести, щоб замокати те, що ми не тестуємо:
# tests/test_size_analysis.py
from unittest.mock import patch
import pytest
from file_system_analyzer.analyzer.size_analysis import calculate_size
from file_system_analyzer.analyzer.errors import DirectoryNotFoundError
@patch('file_system_analyzer.analyzer.size_analysis.os.path.isdir')
@patch('file_system_analyzer.analyzer.size_analysis.traverse_directory')
@patch('file_system_analyzer.analyzer.size_analysis.categorize_file')
@patch('file_system_analyzer.analyzer.size_analysis.os.path.getsize')
def test_calculate_size(mock_getsize, mock_categorize_file, mock_traverse_directory, mock_isdir):
mock_isdir.return_value = True
mock_traverse_directory.return_value = ['/path/to/file1.txt', '/path/to/file2.jpg']
mock_categorize_file.side_effect = ['text', 'image']
mock_getsize.side_effect = [100, 200]
result = calculate_size('/path/to/directory')
assert result == {'text': 100, 'image': 200}
def test_calculate_size_directory_not_found():
with pytest.raises(DirectoryNotFoundError):
calculate_size('/invalid/directory')
Переміщаємось до останнього методу нашого додатку — перевірка незвичних дозволів. Знову базове тіло та тестові випадки:
# file_system_analyzer/analyzer/permissions.py
def check_permissions(directory):
pass
# tests/test_size_permissions.py
from unittest.mock import patch
import pytest
from file_system_analyzer.analyzer.permissions import check_permissions
from file_system_analyzer.analyzer.errors import DirectoryNotFoundError
def test_check_permissions():
result = check_permissions('/path/to/directory')
assert result == ['/path/to/file1.txt', '/path/to/file2.sh']
def test_check_permissions_directory_not_found():
with pytest.raises(DirectoryNotFoundError):
check_permissions('/invalid/directory')
Ось кінцевий вигляд функції check_permissions
:
# file_system_analyzer/analyzer/permissions.py
import os
import stat
from file_system_analyzer.analyzer.traversal import traverse_directory
from file_system_analyzer.analyzer.errors import DirectoryNotFoundError
def check_permissions(directory):
if not os.path.isdir(directory):
raise DirectoryNotFoundError(
f"The specified directory '{directory}' does not exist or is not a directory."
)
unusual_permissions = []
for file_path in traverse_directory(directory):
st = os.stat(file_path)
if bool(st.st_mode & stat.S_IWOTH):
unusual_permissions.append(file_path)
return unusual_permissions
І ось як виглядає фінальний тест, знову замоканий для того, що ми не тестуємо:
# tests/test_size_permissions.py
from unittest.mock import patch
import pytest
from file_system_analyzer.analyzer.permissions import check_permissions
from file_system_analyzer.analyzer.errors import DirectoryNotFoundError
@patch('file_system_analyzer.analyzer.permissions.os.path.isdir')
@patch('file_system_analyzer.analyzer.permissions.traverse_directory')
@patch('file_system_analyzer.analyzer.permissions.os.stat')
def test_check_permissions(mock_stat, mock_traverse_directory, mock_isdir):
mock_isdir.return_value = True
mock_traverse_directory.return_value = ['/path/to/file1.txt', '/path/to/file2.sh']
mock_stat.return_value.st_mode = 0o777
result = check_permissions('/path/to/directory')
assert result == ['/path/to/file1.txt', '/path/to/file2.sh']
def test_check_permissions_directory_not_found():
with pytest.raises(DirectoryNotFoundError):
check_permissions('/invalid/directory')
Зачекайте, майже готово.
Тепер, коли всі робочі частини розроблено, ми більш ніж готові об'єднати все в один модуль і зробити це справжнім інструментом командного рядка. Побачимося в Частині 4, де ми нарешті побачимо мету всього, над чим ми працювали. До зустрічі там!
Перекладено з: File System Analyzer on Python with TDD step by step (Part 3)