TGViewer
Channel Public Channel
Валерий | AQA Engineer | Автотестирование на Python | REST, gRPC, GraphQL

Валерий | AQA Engineer | Автотестирование на Python | REST, gRPC, GraphQL

@aqa_engineer

Сделаю из тебя крутого AQA инженера на Python.

• Преподаю лучшие тренинги по автоматизации тестирования API
• Senior Python developer | AQA lead, 7 лет в IT
Subscribers
1.52K
Photos
167
Videos
151
Links
179

Showing posts older than #431 · Back to latest

Older Posts 14 shown
Post #430 1.04K
#solid
📌 D – Dependency Inversion Principle (DIP)
Принцип инверсии зависимостей

“Зависимости должны быть от абстракций, а не от конкретных реализаций.”

В DIP помогает строить гибкие, легко тестируемые и переиспользуемые компоненты.
Особенно когда нужно подменить авторизацию, логирование, сделать allure-разметку или сбор coverage

В чём суть?

Класс не должен напрямую зависеть от конкретной реализации, он должен зависеть от интерфейса (абстракции).

🧪 Пример: плохая реализация (без DIP)

Скажу честно, сам я тоже когда-то грешил завязываясь на конкретную реализацию клиента, в данном случае requests.Session()

class ApiClient:
def __init__(self):
self.session = requests.Session()

def get_user(
self,
user_id
) -> dict:
response = self.session.get(
f"{API_URL}/user/{user_id}"
)
return response.json()


Проблемы:
• Жёсткая зависимость от requests, если мы захотим использовать, например httpx, мы не сможем этого сделать без изменения кода.
• Нельзя замокать поведение в тесте
• Нарушен DIP: клиент зависит от деталей

✅ Как сделать правильно (с применением DIP)
То есть говорим какие методы обязательно должны быть у принимаемого на вход клиента.
В Python нет интерфейсов в классическом понимании, поэтому abc.ABC и @abstractmethod — это абстрактные классы, которые эмулируют интерфейсы.

1. Вводим интерфейсы:
from abc import ABC, abstractmethod

class BaseHttpClient(ABC):
@abstractmethod
def get(
self,
url: str,
**kwargs: Any
) -> dict:
pass


2. Реализация:

Наследуемся и описываем методы соблюдая контракт, "контракт" — это обязательство реализовать методы, объявленные в абстракции (BaseHttpClient).

При соблюдении этих правил, любой клиент, который соблюдает контракт, может быть безболезнено (в идеале) использован.

class HttpClient(BaseHttpClient):
def get(
self,
url: str,
**kwargs: Any
) -> dict:
return requests.get(url, **kwargs).json()


3. Внедряем зависимости:

То есть любой наследник BaseHttpClient не должен поломать код.
class ApiClient:
def __init__(
self,
http_client: BaseHttpClient
):
self.http_client = http_client

def get_user(self, user_id, headers) -> dict:
return self.http_client.get(
f"{API_URL}/user/{user_id}",
headers=headers,
).json()


🧪 Теперь в тесте:

# Мы можем или замокать клиент тем самым написать тест без реального апи
class MockHttpClient(BaseHttpClient):
def get(self, url, headers):
return {"id": 123, "name": "Test User"}

def test_get_user():
client = ApiClient(MockHttpClient())
user = client.get_user(123)
assert user["id"] == 123


# Можем добавить свою логику, например логгирование запроса
class LoggedHttpClient(BaseHttpClient):
def get(
self,
url,
headers
) -> dict:
print(url, headers)
response = self.http_client.get(
url,
headers=headers
)
print(response.text)
return response.json()


# И здесь тест уже будет с логами
def test_get_logged_user():
client = ApiClient(LoggedHttpClient())
user = client.get_user(123)
assert user["id"] == 123


Профит:
• Подменили зависимости без боли
• Не нужен реальный backend для тестов
• Код клиента не меняется — только зависимости

DIP особенно полезен когда:
• Пишешь интеграционные и контрактные тесты
• Нужно мокать внешние сервисы
• В проекте много обвязки (логов, ретраев, токенов) — и ты хочешь это всё отделить
• Требуется расширяемость (добавить кеш, валидацию, другой http-клиент)

Вывод

Применение DIP в API‑тестах:
🔹 Упрощает подмену зависимостей
🔹 Повышает читаемость и тестируемость
🔹 Позволяет изолировать бизнес‑логику от инфраструктуры
🔹 Снижает связанность компонентов

🔥Перешли другу\коллеге пусть тоже знает)

На этом мы закрываем тему SOLID. )


——————————-

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

📱 Обучение

📱 Отзывы
  • 🔥 5
  • ❤ 1
  • 👍 1
Post #429
Валерий | AQA Engineer | Автотестирование на Python | REST, gRPC, GraphQL pinned «🎉 Держу слово! Как и обещал, у меня есть 2 бесплатные проходки на E-CODE Напомню, что это за событие: • Крутые спикеры и технические доклады (включая мой 😎) • Отличный нетворкинг с коллегами по цеху • Очень. Много. Еды. 🍕 • Возможность погрузиться в мир современных…»
Post #428 945
Кнопочку не забываем жмякать на посте)
  • 🤝 5
Post #427 1.32K
🎉 Держу слово! Как и обещал, у меня есть 2 бесплатные проходки на E-CODE

Напомню, что это за событие:
• Крутые спикеры и технические доклады (включая мой 😎)
• Отличный нетворкинг с коллегами по цеху
• Очень. Много. Еды. 🍕
• Возможность погрузиться в мир современных технологий

📋 Условия участия в розыгрыше:
1️⃣ Быть подписанным на мой канал
2️⃣ Быть подписанным на @ozon_tech
3️⃣ Поставить ❤️ под этим постом


🗓 Розыгрыш проведу в следующий понедельник — 01.09
Победителей выберу рандомом среди всех участников.

Участвуйте, друзья! Это отличная возможность попасть на качественное техническое мероприятие и лично пообщаться 🤝

Participants: 50
Prizes: 2
Giveaway date: 13:00, 01.09.2025 MSK (completed)

The winners of the giveaway:
1. Mariya Kramer - 3s7hle
2. Юлия - 3rajci
  • ❤ 44
  • ✍ 2
Post #426 893
#solid
📌 I – Interface Segregation Principle (ISP)

Принцип разделения интерфейсов

“Не заставляй клиента зависеть от того, что он не использует.”

В автоматизации это часто встречается, когда в один «удобный» хелпер пихают вообще всё подряд — и API, и UI, и БД.

🧪 Пример

Представим, что кто-то решил упростить жизнь и создал “универсальный” интерфейс:
class TestHelper:
def get_sms_code_by_api(self): ...
def login_from_ui(self): ...
def check_user_created_in_db(self): ...
def get_kafka_message(self): ...


Теперь любой тест, независимо от контекста, тянет за собой весь этот набор.
• Тесты на API зависят от методов, которые они не используют
• UI‑тесты вынуждены тащить зависимости от kafka или БД
• Такие классы очень быстро увеличиваются в размерах, их становится тяжело поддерживать.

Нарушен ISP — мы создали жирный интерфейс, который знает всё и сразу.

✅ Как исправить?

Разделить интерфейсы по зонам ответственности:
class ApiHelper:
def get_sms_code(self): ...

class UiHelper:
def login(self): ...

class KafkalHelper:
def get_message(self): ...

class DbHelper:
def check_user_created(self): ...


Теперь каждый тест получает только то, что ему реально нужно:
def test_ui_login(ui: UiHelper):
ui.login()

def test_registration_in_db(db: DbHelper):
...
db.check_user_created()


Как правильно сделать удобный хэлпер, не нарушая принцип ISP?

class TestHelper:
def __init__(
self,
ui: UiHelper,
db: DbHelper,
kafka: KafkalHelper,
api: ApiHelper
) -> None:
self.ui = ui
self.kafka = kafka
self.db = db
self.api = api


Теперь TestHelper — это фасад, который объединяет несколько интерфейсов, но не нарушает ISP, потому что:

1. Клиенты (тесты) зависят только от того, что используют – если тест работает только с UI, он может обращаться только к helper.ui, не зная о других модулях.

2. Интерфейсы внутри фасада разделены – UiHelper, ApiHelper и другие остаются независимыми.

Теперь мы используем только то что нужно, но при этом не нарушаем ISP и тест выглядит так.

def test_user_registration(helper: TestHelper):
helper.ui.login()
helper.api.get_sms_code()
helper.kafka.get_message()
helper.db.check_user_created()


🔧 Когда мы соблюдаем ISP то:
• Проще вникнуть в небольшие независимые классы
• Проще переиспользовать компоненты
• Удобно если много интеграций API, UI, DB

Вывод:

Соблюдение ISP делает код:
🔹 Более модульным — только нужные зависимости
🔹 Легче тестировать
🔹 Проще поддерживать, меньше случайных поломок


——————————-

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

📱 Обучение

📱 Отзывы
  • 🔥 5
  • ❤ 3
  • 🙏 1
Post #425 1K
Привет! Много чего навалилось, и давненько не выкладывал постов 😅

Сейчас активно готовлюсь к докладу на E-CODE.
Вчера был тестовый прогон, будет ещё два.

Нужно править презентацию, параллельно закрывать таски на работе и, естественно, не забывать вести группу, которая у меня сейчас учится.

Поэтому активностей много — сижу работаю до ночи 🌙

Из хороших новостей: недавно вы просили проходки на E-CODE.
А я прислушиваюсь к своим подписчикам и сходил к нашим организаторам — пока пообещали 2 проходки, которые, если всё сложится, будут разыграны в моём канале! 🎉

Почему стоит поучаствовать?
• Крутые спикеры (в том числе и я) и доклады
• Крутая атмосфера и нетворкинг
• И как написано в приглашении — Очень. Много. Еды. 🍕

Как только всё согласуют с маркетингом, обещаю вернуться и сделать розыгрыш проходок!

Билеты на E-CODE именные по документам, поэтому просто так туда не попадёшь.
  • 🔥 8
  • 😁 1
Post #420 923
ВТОРАЯ НЕДЕЛЯ ТРЕНИНГА — ЗАВЕРШЕНА!

Получил домашки от студентов, и результат классный!

✅ Что освоили наши студенты за вторую неделю:
- Внедрили архитектурные паттерны в тестовый фреймворк
- Настроили Proxy для детального логгирования API-запросов

- Реализовали Facade для упрощения работы с клиентами
- Добавили Decorator для автоматической обработки ошибок и retry-логики
- Освоили генерацию динамических тестовых данных через фикстуры
- Внедрили параметризацию и Data-driven подход в тесты

💡 Результат превзошёл ожидания:
Студенты действительно превратили "не очень красивые тесты" в элегантный, масштабируемый код! Архитектура стала читаемой, поддерживаемой и готовой к росту.

Если вы сравните код из прошлой недели, то почувствуете разницу)

ЧТО ДАЛЬШЕ?

Третья неделя будет посвящена системе проверок:
🎯 Комплексная система валидации
- Виды проверок и их применение
- Базовые проверки ответов API
- Проверка структуры и типов данных JSON
- Валидация значений в JSON структурах

🎯 Продвинутые техники проверок
- Менеджеры контекста для группировки проверок
- Мягкие проверки (soft assertions)
- Создание собственных функций-чекеров

Цель: Построить bulletproof систему валидации, которая найдёт любую проблему в API! 🎯

Уже сейчас видно, что студенты получают очень крутые навыки AQA automation engineer!

Всем отличных выходных, увидимся на третьей неделе! 💪

——————————-

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

📱 Обучение

📱 Отзывы
  • 🔥 4
Post #419 866
Привет, озону даже не нужно платить мне за рекламу)

Почему?

Потому что мой доклад взяли в программу конференции E-CODE

Очень крутая конференция всем рекомендую.

А я со своей стороны приглашаю вас туда, может познакомимся с кем то лично)

На предстоящей конференции буду рассказывать про:
✅ Восстановление полной схемы gRPC-сервиса через рефлексию
✅ Собственный плагин для protoc с удобными Python-клиентами
✅ Собственную open source библиотеку BPReflect для автоматизации всего workflow.

Работа с gRPC станет для вас изи каткой.

Так что велкам)
Приходи и поделись постом с друзьями.

——————————-

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

📱 Обучение

📱 Отзывы
  • 🔥 18
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
Post #417 697
📚 Новый учебный проект: Awesome Tests Project

Как преподаватель API тестирования часто сталкиваюсь с вопросом:
"Как правильно структурировать большой тестовый проект?"

Создал open-source пример того, как я вижу масштабируемую архитектуру интеграционных тестов:

🎯 Особенности:
• Планируется поддержка множества технологий (HTTP, gRPC, DB, Redis, Kafka), на текущий момент можно увидеть только HTTP, остальное в планах.
• Четкое разделение фреймворка и тестов
• Переиспользуемые компоненты
• Готовность к росту команды и проекта
• В проекте я использую собственные библиотеки

Проект больше основан как монорезозиторий для всего app, сделано это для того, чтобы в одном проекте соединить все базовые технологии.

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

💻 Технологии:
Python 3.11+, Httpx, Pytest, Pydantic, Poetry + собственные библиотеки для генерации кода

Проект создан для изучения и как reference implementation.
Все исходники открыты, документация внутри.

🔗 https://github.com/ValeriyMenshikov/awesome-tests-project

Сохраняйте в закладки и ставьте звездочку!

——————————-

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

📱 Обучение

📱 Отзывы
  • 👍 15
  • 🔥 11
  • ❤ 3
Post #413 719
Привет))

ПЕРВАЯ НЕДЕЛЯ ТРЕНИНГА — ЗАВЕРШЕНА!

Ну и приложил скрины работы, одного из студентов)

✅ Что успели наши студенты за 5 дней:
- Создали базовую структуру тестового фреймворка
- Выделили API-клиенты в отдельные модули и научились работать с API почтового сервера.
- Написали тесты на первые 4 хэндлера API
- Освоили работу с JSON-парсингом
- Интегрировали тесты в CI/CD пайплайн


💡 Результат:
За одну неделю студенты прошли объём почти любого базового тренинга по API-тестированию


ЧТО ДАЛЬШЕ?

Следующая неделя будет ещё интереснее:
🎯 Архитектурные паттерны
- Proxy для API логгирования
- Facade для удобства использования клиентов
- Decorator для повторной обработки ошибок и ожиданий

🎯 Генерация тестовых данных
- Фикстуры для создания динамических данных
- Параметризация тестов
- Data-driven подход


Цель:
Превратить "не очень красивые тесты" в красивые))

Результат уже хороший, после выходных буду ждать новых домашек, следующий отчет по итогам второй недели будет так же в пятницу)

Ну а всем остальным желаю ХОРОШИХ ВЫХОДНЫХ))

——————————-

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

📱 Обучение

📱 Отзывы
  • 🔥 12
Post #412 732
#solid
O – Open/Closed Principle (OCP)

Код должен быть открыт для расширения, но закрыт для модификации.

Рассмотрим пример из предыдущего поста, где KafkaProducerManager принимает продюсер в конструкторе. Это один из примеров OCP в действии.

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

Допустим, нам нужно:
- Добавить логирование отправляемых сообщений
- Реализовать авторизацию при отправке в топик
- Ввести валидацию сообщений

Старая реализация (без OCP) потребовала бы изменений в KafkaProducerManager что добавляет риск сломать существующую логику.

Новая реализация (с OCP) 
позволяет расширить функционал без модификации основного класса.

Реализация:

1. Определяем базовый интерфейс
:
from abc import ABC, abstractmethod

class BaseProducer(ABC):
    @abstractmethod
    def send(self, message: type[BaseModel]) -> None:
        pass


2. Реализуем конкретный продюсер:

class LoggedProducer(BaseProducer):
def __init__(self, producer: KafkaProducer):
self.producer = producer

def send(self, message: str) -> None:
        print(f"Отправка сообщения: {message}")
        return self.producer.send(message)



3. Модифицируем менеджер
:

Изменяем аннотацию указывая, что теперь принимаем любой совместимый продюсер
class KafkaProducerManager:
def __init__(self, producer: BaseProducer)
self.producer = producer

def send_message(self, message: type[BaseModel]):
msg = message.model_dump_json()
return self.producer.send(msg)


4. 💻 Пример использования:
# Базовый продюсер
producer = KafkaProducer()

# Расширенный функционал (логирование)
logged_producer = LoggedProducer(producer)

# Менеджер работает с любым продюсером
producer_manager = KafkaProducerManager(logged_producer)

message = Message(body='some value', key='some_key')
producer_manager.send(message) # Логируется и отправляется сообщение


Преимущества:
1. Безопасность - Не трогаем рабочий код
2. Гибкость 
- Легко добавляем новые фичи
3. Масштабируемость 
- Можно создать ValidatedProducer, AuthProducer и т.д.


👉 Главное: Мы расширили функционал, не изменяя существующий, код закрыт для модификации.
Именно в этом суть OCP.

——————————-

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

📱 Обучение

📱 Отзывы
  • 👍 3
Post #411 930
Группа набралась. Кто не успел - следите за анонсами

✅ НАБОР ЗАКРЫТ

Группа REST API Advanced набралась за 2 дня после официального объявления регистрации.

Несколько человек взяли полный пакет REST API Advanced + REST API Professional

10 мест заняли те, кто не откладывал решение на потом.

Для тех, кто не успел:
Я понимаю, что кто-то колебался, кто-то ждал "лучшего момента", кто-то думал "ещё успею".
Но рынок не ждёт. Пока одни думают, другие действуют.

Что будет дальше:
🎯 Следующие 2 месяца - интенсивная работа с группой
🎯 44 урока Advanced + Professional, сотни часов практики, персональная обратная связь
🎯 К концу октября у меня будет ещё несколько экспертов уровня SDET


Следующий поток планирую в ноябре. Но:
• Возможно, сокращу количество мест чтобы было более комфортно до Нового года.

Те, кто попал в эту группу, получат преимущество на рынке на 4 месяца раньше.

А 4 месяца в текущих реалиях - это очень много.

Обязательно подпишитесь на уведомления канала 🔔

Анонс следующего потока будет только здесь.

И помните: возможности не ждут, когда вы будете готовы к ним.

P.S. Тем, кто всё-таки решится на самостоятельное изучение - уважаю.
Главное не останавливайтесь в развитии. Рынок прощает многое, но не стагнацию.


А пока продолжу постить технический контент, я помню что обещал перевыложить посты по SOLID.
Older posts →
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 →