← Назад к вопросам
Влияет ли грамотность написания кода на возможность тестирования?
2.3 Middle🔥 241 комментариев
#Архитектура и паттерны#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Влияет ли грамотность кода на тестируемость?
Да, критически важно! Качество кода напрямую влияет на возможность и сложность тестирования.
Плохой код - сложно тестировать
Пример 1: Тесная связанность
# ПЛОХО: класс зависит от конкретных реализаций
class UserService:
def __init__(self):
self.db = PostgreSQL() # Прямая зависимость!
self.email = GmailSender() # Прямая зависимость!
def create_user(self, email):
user = self.db.save({"email": email}) # Реальная БД
self.email.send("Welcome") # Реальная почта
return user
# Невозможно тестировать без реальной БД и почты!
Пример 2: Смешанная логика
# ПЛОХО: бизнес логика смешана с деталями реализации
def process_payment(amount):
# Валидация
if amount <= 0:
raise ValueError()
# Доступ в БД
conn = sqlite3.connect("db.sqlite3")
cursor = conn.cursor()
cursor.execute("INSERT INTO payments ...")
# HTTP запрос
response = requests.post("https://payment-api.com/charge", ...)
# Логирование
with open("/var/log/payments.log", "a") as f:
f.write(f"Payment: {amount}")
return response.json()
# Где начинается логика? Где её тестировать?
Хороший код - легко тестировать
Пример 1: Инъекция зависимостей
# ХОРОШО: зависимости передаются
class UserService:
def __init__(self, db, email_sender):
self.db = db # Может быть real или mock
self.email = email_sender # Может быть real или mock
def create_user(self, email):
user = self.db.save({"email": email})
self.email.send("Welcome")
return user
# В тестах
class MockDB:
def save(self, data):
return {"id": 1, **data}
class MockEmail:
def send(self, message):
pass
# Легко тестировать!
db = MockDB()
email = MockEmail()
service = UserService(db, email)
user = service.create_user("john@example.com")
assert user["id"] == 1
Пример 2: Разделение ответственности
# ХОРОШО: каждый класс отвечает за одно
class PaymentValidator:
def validate(self, amount):
if amount <= 0:
raise ValueError("Invalid amount")
return True
class PaymentProcessor:
def __init__(self, gateway):
self.gateway = gateway
def process(self, amount):
return self.gateway.charge(amount)
class PaymentLogger:
def log(self, amount, status):
print(f"Payment: {amount}, Status: {status}")
# Тестируем каждую часть отдельно
validator = PaymentValidator()
assert validator.validate(100) == True
gateway = MockGateway()
processor = PaymentProcessor(gateway)
assert processor.process(100)["status"] == "success"
logger = PaymentLogger()
logger.log(100, "success") # Легко тестировать
SOLID принципы улучшают тестируемость
Single Responsibility
# ПЛОХО: один класс делает всё
class Order:
def calculate_total(self): # Расчёт
pass
def save_to_db(self): # Сохранение
pass
def send_email(self): # Отправка
pass
def generate_pdf(self): # PDF генерация
pass
# ХОРОШО: каждый класс за одно
class OrderCalculator:
def calculate_total(self):
pass
class OrderRepository:
def save(self):
pass
class OrderNotifier:
def send_email(self):
pass
class InvoiceGenerator:
def generate_pdf(self):
pass
Dependency Inversion
# ПЛОХО: зависит от конкретной реализации
class PaymentService:
def __init__(self):
self.payment_gateway = StripeGateway() # Конкретный класс!
# ХОРОШО: зависит от абстракции
from abc import ABC, abstractmethod
class PaymentGateway(ABC):
@abstractmethod
def charge(self, amount): pass
class PaymentService:
def __init__(self, gateway: PaymentGateway): # Абстракция
self.gateway = gateway
# В тестах
class TestPaymentGateway(PaymentGateway):
def charge(self, amount):
return {"status": "success"}
service = PaymentService(TestPaymentGateway())
Практические примеры тестирования
До улучшений (невозможно тестировать)
# user_service.py
import requests
import sqlite3
def register_user(email, password):
# Валидация
if not email or not password:
return {"error": "Invalid input"}
# Доступ в БД
conn = sqlite3.connect("app.db")
cursor = conn.cursor()
cursor.execute("INSERT INTO users (email, password) VALUES (?, ?)",
(email, password))
conn.commit()
# Отправка письма
response = requests.post("https://email-api.com/send",
data={"to": email, "subject": "Welcome"})
return {"status": "success"}
# Тест невозможен без реальной БД и API!
После улучшений (легко тестировать)
# domain/user.py - Бизнес логика
class User:
def __init__(self, email, password):
if not email or not password:
raise ValueError("Invalid input")
self.email = email
self.password = password
# application/user_service.py - Use cases
class UserService:
def __init__(self, user_repository, email_service):
self.repository = user_repository
self.email = email_service
def register(self, email, password):
user = User(email, password) # Валидация в domain
self.repository.save(user) # Сохранение через интерфейс
self.email.send_welcome(email) # Отправка через интерфейс
return user
# tests/test_user_service.py
class MockRepository:
def save(self, user):
self.saved_user = user
return user
class MockEmail:
def send_welcome(self, email):
self.emails.append(email)
def test_register_user():
repo = MockRepository()
email = MockEmail()
service = UserService(repo, email)
user = service.register("john@example.com", "password123")
assert user.email == "john@example.com"
assert repo.saved_user == user
assert "john@example.com" in email.emails
Признаки плохо тестируемого кода
- Жёсткие зависимости - зависит от конкретных классов
- Глобальное состояние - использует глобальные переменные
- Побочные эффекты - вызывает внешние API в функции
- Смешанная логика - бизнес логика + деталь реализации
- Большие объекты - класс делает слишком много
- Сложные конструкторы - много зависимостей, сложно создать
- Приватные методы - невозможно тестировать части
- Статические методы - сложно мокировать
Инструменты тестирования
# unittest (встроенный)
import unittest
class TestUserService(unittest.TestCase):
def test_register(self):
pass
# pytest (популярный)
import pytest
def test_register():
assert True
# pytest-mock (мокирование)
from unittest.mock import Mock, patch
@patch("requests.post")
def test_with_mock(mock_post):
mock_post.return_value = {"status": "success"}
# Тестируем с мокированным запросом
Метрики тестируемости
- Code Coverage: > 90% (idealistically)
- Cyclomatic Complexity: < 10 (простые функции)
- Dependency Injection: >= 80% (минимум зависимостей)
- Unit Tests: один на функцию/метод
- Integration Tests: один на сценарий
Итоговый вывод
Грамотность кода ПРЯМО влияет на тестируемость:
- Инъекция зависимостей → легко мокировать
- Разделение ответственности → легко тестировать части
- Чистая архитектура → нет побочных эффектов
- SOLID принципы → гибко и тестируемо
Плохой код - значит никакие тесты не спасут! Хороший код - легко тестировать, меньше багов в production.