Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
TDD (Test-Driven Development)
TDD — методология разработки, при которой тесты пишут ДО написания кода. Цикл: RED → GREEN → REFACTOR.
Цикл TDD
1. RED: Напиши падающий тест
import pytest
from calculator import add
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0
# Запускаем: pytest
# FAIL: ModuleNotFoundError: No module named 'calculator'
2. GREEN: Напиши минимальный код для прохождения теста
# calculator.py
def add(a, b):
return a + b
# Запускаем: pytest
# PASSED: все тесты зелены
3. REFACTOR: Улучши код, сохраняя тесты зелёными
# Добавь валидацию, документацию, оптимизацию
def add(a: int, b: int) -> int:
"""Складывает два числа"""
if not isinstance(a, (int, float)):
raise TypeError(f"Expected number, got {type(a)}")
if not isinstance(b, (int, float)):
raise TypeError(f"Expected number, got {type(b)}")
return a + b
# Запускаем: pytest
# PASSED: всё ещё работает
Пример: функция для валидации email
Шаг 1: RED (падающие тесты)
import pytest
from validator import is_valid_email
class TestEmailValidator:
def test_valid_email(self):
assert is_valid_email("user@example.com") == True
assert is_valid_email("john.doe@company.co.uk") == True
def test_invalid_email(self):
assert is_valid_email("invalid.email") == False
assert is_valid_email("@example.com") == False
assert is_valid_email("user@") == False
def test_empty_string(self):
assert is_valid_email("") == False
# pytest test_validator.py
# FAILED: is_valid_email is not defined
Шаг 2: GREEN (минимальный код)
# validator.py
def is_valid_email(email: str) -> bool:
return "@" in email and "." in email
# pytest
# 3 passed
Шаг 3: REFACTOR (улучшение)
import re
def is_valid_email(email: str) -> bool:
"""Проверяет валидность email адреса"""
if not email or not isinstance(email, str):
return False
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
return bool(re.match(pattern, email))
# pytest
# 3 passed (тесты всё еще проходят)
TDD в ML/Data Science
# Тест для функции обработки данных
def test_data_preprocessing():
raw_data = pd.DataFrame({
'age': [25, None, 35, 40],
'salary': [50000, 60000, None, 80000]
})
processed = preprocess_data(raw_data)
# Проверяем удаление пропусков
assert processed.isnull().sum().sum() == 0
# Проверяем размер
assert len(processed) == 2
# Проверяем типы
assert processed['age'].dtype in [np.float64, np.int64]
# RED: функция не существует
# GREEN: минимальная реализация
def preprocess_data(df):
return df.dropna()
# REFACTOR: добавь валидацию, логирование
def preprocess_data(df: pd.DataFrame) -> pd.DataFrame:
"""Удаляет строки с пропусками"""
if df.empty:
raise ValueError("Input DataFrame is empty")
initial_rows = len(df)
df_clean = df.dropna()
dropped = initial_rows - len(df_clean)
print(f"Removed {dropped} rows with missing values")
return df_clean
Преимущества TDD
✓ Меньше багов: тесты ловят проблемы рано
✓ Документация: тесты показывают usage
✓ Рефакторинг: можешь менять код, не боясь
✓ Дизайн: думаешь о API перед реализацией
✓ Confidence: знаешь, что код работает
Недостатки TDD
✗ Медленнее писать: тесты + код
✗ Для exploratory работы плохо (notebooks)
✗ Трудно начать (нужна практика)
✗ Мертвые тесты: если требования меняются
TDD vs коде-сначала разработка
| Аспект | TDD | Code-First |
|---|---|---|
| Когда писать | Тесты→Код | Код→Тесты |
| Покрытие | ~95%+ | ~40-60% |
| Багов | Меньше | Больше |
| Скорость | Медленнее | Быстрее |
| Рефакторинг | Легче | Рискованнее |
Пример в контексте ML модели
# Тест (RED)
def test_model_prediction():
model = MyModel()
X = np.array([[1, 2], [3, 4], [5, 6]])
predictions = model.predict(X)
assert predictions.shape == (3,) # один предсказание на вход
assert np.all((predictions >= 0) & (predictions <= 1)) # вероятности
# Код (GREEN)
class MyModel:
def predict(self, X):
return np.random.random(len(X))
# REFACTOR
class MyModel:
def __init__(self, weights=None):
self.weights = weights or np.random.randn(2)
def predict(self, X):
logits = X @ self.weights
return 1 / (1 + np.exp(-logits)) # sigmoid
def train(self, X, y, epochs=100):
for _ in range(epochs):
predictions = self.predict(X)
loss = -np.mean(y * np.log(predictions + 1e-10) +
(1-y) * np.log(1-predictions + 1e-10))
# gradient descent...
Best Practices
# 1. Один assert на тест (или группируй логичные)
def test_string_methods():
assert "hello".upper() == "HELLO"
assert "hello".capitalize() == "Hello"
# 2. Названия тестов описывают поведение
def test_add_positive_numbers():
assert add(2, 3) == 5
def test_add_negative_numbers():
assert add(-2, -3) == -5
# 3. Используй pytest fixtures
@pytest.fixture
def sample_data():
return pd.DataFrame({'a': [1, 2, 3]})
def test_with_fixture(sample_data):
assert len(sample_data) == 3
# 4. Тестируй edge cases
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
Резюме
TDD:
- Пишем тесты ДО кода
- Цикл: RED → GREEN → REFACTOR
- Меньше багов, лучший дизайн
- Требует дисциплины и практики
- Ideal для production кода, менее ideal для research