Валерий | AQA Engineer | Автотестирование на Python | REST, gRPC, GraphQL
1.48K subscribers
163 photos
149 videos
1 file
173 links
Сделаю из тебя крутого AQA инженера на Python.

• Преподаю лучшие тренинги по автоматизации тестирования API
• Senior Python developer | AQA lead, 7 лет в IT
Download Telegram
Привет))

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

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

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


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


ЧТО ДАЛЬШЕ?

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

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


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

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

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

——————————-

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

📱 Обучение

📱 Отзывы
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12
📚 Новый учебный проект: 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-сообщество

📱 Обучение

📱 Отзывы
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥113
#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-сообщество

📱 Обучение

📱 Отзывы
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6
Привет, озону даже не нужно платить мне за рекламу)

Почему?

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

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

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

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

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

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

——————————-

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

📱 Обучение

📱 Отзывы
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18
ВТОРАЯ НЕДЕЛЯ ТРЕНИНГА — ЗАВЕРШЕНА!

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

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

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

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

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

ЧТО ДАЛЬШЕ?

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

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

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

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

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

——————————-

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

📱 Обучение

📱 Отзывы
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
Привет! Много чего навалилось, и давненько не выкладывал постов 😅

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

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

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

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

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

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

Билеты на E-CODE именные по документам, поэтому просто так туда не попадёшь.
🔥8😁1
#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. Интерфейсы внутри фасада разделены – UiHelperApiHelper и другие остаются независимыми.

Теперь мы используем только то что нужно, но при этом не нарушаем 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-сообщество

📱 Обучение

📱 Отзывы
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥53🙏1
🎉 Держу слово! Как и обещал, у меня есть 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
442
Кнопочку не забываем жмякать на посте)
🤝5
Валерий | AQA Engineer | Автотестирование на Python | REST, gRPC, GraphQL pinned «🎉 Держу слово! Как и обещал, у меня есть 2 бесплатные проходки на E-CODE Напомню, что это за событие: • Крутые спикеры и технические доклады (включая мой 😎) • Отличный нетворкинг с коллегами по цеху • Очень. Много. Еды. 🍕 • Возможность погрузиться в мир современных…»
#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-сообщество

📱 Обучение

📱 Отзывы
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥51👍1
🎉 РОЗЫГРЫШ ЗАВЕРШЕН!

Друзья, розыгрыш 2-х бесплатных проходок на E-CODE завершился!

Поздравляю победителей:
🏆 @Mariya_Kramer

🏆 @saramagaziner

Победители, напишите мне в личные сообщения — отправлю вам детали по получению билетов на конференцию!

Всем огромное спасибо за участие! ❤️

Тем, кто не выиграл — не расстраивайтесь! Следите за каналом, будут еще интересные розыгрыши и полезный контент по API тестированию 🚀

Увидимся на E-CODE! 👋

P.S. Победители, не забудьте написать в ЛС в течение 48 часов, иначе выберу новых победителей 😉
👍3
Привет всем! 👋

Соскучился по вам, ребята!
Было просто пздц много дел последнее время - выпустил очередную группу с курса, закончил с подготовкой презентации на E-CODE.

Напоминаю, что уже 13 сентября (это суббота!) будет E-CODE конференция - отличная возможность познакомиться со многими из вас лично!
Кто планирует быть? 🤝

И так как сегодня 9 сентября - День тестировщика, то поздравляю всех качественных ребят)))!

Пусть баги находятся легко, тест-кейсы проходят зеленым, а продакшн не падает по выходным! 😄
20🤝5
🎉 С Днём программиста! 🎉

С участием в E-CODE совсем забыл сказать, чему он приурочен - а именно Дню программиста!

Программирование - одно из самых увлекательных занятий.
Уже несколько лет живу на дофамине от решения рабочих задач.

Когда ковыряешься в коде неделями и наконец решаешь задачу - всегда есть легкая эйфория от того, что преодолел новое препятствие и справился!

В современном мире этот навык позволяет автоматизировать свою деятельность и облегчить жизнь - будь то телеграм-бот или парсер для поиска лучших цен на отели и билеты

- Отличная профилактика болезни Альцгеймера ))
- А разработка - одна из интереснейших ветвей развития Automation QA

Кайфую от момента, когда создаешь что-то, что экономит кучу времени тебе и команде.

И да, это единственная профессия, где можно сломать всё одной строчкой кода, а потом героически это же исправить другой строчкой 😅

Поздравляю всех причастных к празднику!

Желаю:
• Интересных задач
• Прекрасных побед 🏆

• Собственных крутых инструментов
• Чтобы продакшн не падал по выходным!
17🔥10👍1
Media is too big
VIEW IN TELEGRAM
Привет, вот и прошел E-CODE, было очень круто.

Познакомился с 4 подписчиками, почувствовал себя звездой 🤣🤣🤣

Тем кто не смог попасть на мой доклад можно посмотреть по ссылке)

Ну и всем хорошего дня)

Даже на видос попал на 10 секунде 😀
🔥17😁72🫡1