TGViewer
Валерий | AQA Engineer | Автотестирование на Python | REST, gRPC, GraphQL Валерий | AQA Engineer | Автотестирование на Python | REST, gRPC, GraphQL @aqa_engineer · 1.52K subscribers
Post #418 771
#solid
📌 L – Liskov Substitution Principle (LSP)

Предыдущий пост.

Принцип подстановки Лисков

«Если в коде вместо базового класса подставить его наследника — поведение не должно ломаться.»

На самом деле было довольно сложно было придумать пример в контексте автотестирования, но давайте попробуем.

🔍 Как это работает?

Допустим, есть базовый API-клиент с аутентификацией:

class BaseApiClient:
# Возвращает токен для аутентификации.
def get_auth_token(self) -> str:
raise NotImplementedError

# Авторизованный запрос
def send_request(self, url: str) -> Response:
token = self.get_auth_token()
headers = {"Authorization": token}
return requests.get(url, headers=headers)


Ок, теперь реализуем двух клиентов:


class AuthClient(BaseApiClient):
def get_auth_token(self) -> str:
return "Bearer token" # Корректный токен

class NoAuthClient(BaseApiClient):
def get_auth_token(self) -> str:
return None # вот тут уже подозрительно


А дальше кто-то пишет код, который работает с клиентом:

def test_send_request(client: BaseApiClient, url: str):
url = "http://some-api.com/endpoint"
return client.send_request(url)


С AuthClient все ок. С NoAuthClient словим баг: {"Authorization": None }.

Почему?

Потому, что поведение наследника не соответствует ожиданиям.


✅ Как правильно:

Если NoAuthClient не поддерживает аутентификацию, не нужно заставлять его реализовывать метод, который делает вид, что всё ок.

Лучше изменить архитектуру:
- вынести токен в отдельный класс (AuthProvider)
- если часть клиентов не поддерживает аутентификацию, вынесем эту логику в отдельный класс/интерфейс:

class AuthProvider(ABC):
@abstractmethod
def get_auth_token(self) -> str:
pass

class Auth(AuthProvider):
def get_auth_token(self) -> str:
return "Bearer token" # Корректный токен

class BaseApiClient:
def send_request(
self,
url: str,
headers: Optional[dict] = None
) -> requests.Response:
return requests.get(url, headers=headers)

class AuthApiClient(BaseApiClient):
def __init__(self, auth: AuthProvider):
self.auth = auth

def send_request(self, url: str, **kwargs) -> requests.Response:
token = self.auth.get_auth_token()
headers = kwargs.get("headers", {})
headers["Authorization"] = token
return super().send_request(url, headers=headers)


Теперь клиент отработает без ошибок.
# и этот клиент
client = AuthApiClient(RealAuth())

# и этот клиент отработает
client = BaseApiClient()


def test_send_request(client: BaseApiClient, url: str):
url = "http://some-api.com/endpoint"
return client.send_request(url)


Вывод:

Соблюдение LSP:
🔹 Делает поведение предсказуемым
🔹 Спасает от скрытых багов
🔹 Позволяет переиспользовать код без костылей


——————————-

📱 TG-сообщество

📱 Обучение

📱 Отзывы
  • 🔥 6
More from @aqa_engineer
  1. Sep 2, 2026💙💙💙💙💙 Маленькое напоминание о большой дате: где стоит провести День программиста⬆️ Ар…
  2. Sep 2, 2026Привет, давно меня не было пригар по работе, что совсем не успеваю ничего. За что прошу пр…
  3. Jul 16, 2026👩‍💻 В мае-июне я проходила курс «Автоматизация тестирования Rest API Advanced (Python)»…
  4. Jun 10, 2026Привет! Уже на следующей неделе у меня стартуют. Автоматизация тестирования брокеров сообщ…
  5. Jun 8, 2026Несколько месяцев назад я спросил, какой технический тренинг вам был бы действительно инте…
  6. Jun 6, 2026Материалы для тех, кто в теме. Одна из самых больших проблем при развитии в IT - это возмо…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →