Валерий | AQA Engineer | Автотестирование на Python | REST, gRPC, GraphQL
Kanalga Telegram’da o‘tish
Сделаю из тебя крутого AQA инженера на Python. • Преподаю лучшие тренинги по автоматизации тестирования API • Senior Python developer | AQA lead, 7 лет в IT
Ko'proq ko'rsatish1 511
Obunachilar
-124 soatlar
+47 kunlar
+830 kunlar
Postlar arxiv
Сейчас я уже стал усатый разработчик и давно не снимаю смешные видео, но свои старые иногда пересматриваю, какие-то они ламповые что ли)
Какая-то атмосфера прикольная, хоть они уже давно не в трендах, но буду наверное периодически постить)
TG-сообщество | Обучение |
Всем привет!
С наступившим Новым годом))
Пока все доедают салаты и допивают алкоголь, я поднял Kafka и написал пару консумеров.
Что же это значит?
Что мои планы по инфраструктуре готовы почти на 100%.
И будут записаны модули по работе с кафка в автотестах.
Ну и напомню, что у меня есть прекрасная статья по кафка)
TG-сообщество | Обучение |
Ребят всем привет 👋
Хотел сделать как и все, написать итоги года, похвастаться какой я молодец.
Год и вправду оказался очень сложный, но насколько продуктивный, что было сделано столько, что впервые за долгое время я подумал, что счастье любит тишину.
Хочу сказать вам спасибо, что доверяете мне как преподавателю, потому что многое, что было сделано, сделано именно благодаря вам.
С некоторыми учениками мы общаемся уже на протяжении нескольких лет и они до сих мне доверяют и готовы дальше со мной сотрудничать как с преподавателем.
Спасибо, что лайкаете мои посты и пишете комментарии, это здорово стимулирует и направляет.
Желаю вам в новом году добиться больших успехов, не только в профессиональном плане, но и просто жизненных, в профессиональном то я вам помогу 😃
Я рад, что вы тут все у меня присутствуете и мы вместе развиваемся)
В общем может я старею, но все чаще мне хочется говорить спасибо за то, что есть, а не просто постоянно просить чего-то))
На этом и закончу, вы все красавчики)) Успехов в новом году)
Про парсинг
Для меня понятие этого слово сейчас уже очевидно, но когда я только начинал прогать я не сильно понимал это слово.
Как сейчас помню, когда я первый раз загуглил это понятие, то вычитал что-то типо "Синтаксический анализ текста".
Ну допустим синтаксический, но допустим анализ, а нахрена мне этот анализ то нужен?
А сейчас, когда я уже написал ни один парсер, я представляю, что это такое.
Самый простой пример, когда мы получаем из автотестов JSON и пытаемся провалидировать какое-то из его значений, по сути мы пишем парсер, который ищет определенное значение по определенному ключу, чем сложнее структура JSON, тем сложнее его парсить.
Но парсинг не ограничивается только чтением готовой структуры. И зачем в принципе уметь парсить автоматизаторам?
Обычным автоматизаторам наверное и не нужно, но если ты хочешь стать крутым челиком и разрабатывать свои инструменты, это почти необходимо.
Сейчас у REST API довольно часто есть OpenAPI схема, у GraphQL своя, у gRPC протофайлы, на основе описания этих документов и используя шаблонизатор можно написать свою кодогенерацию, например, для генерации смоук теста на jinja, надо сделать примерно следующий шаблон.
class Tests{{ method|capitalize }}{{ path|to_camel_case }}(BaseSetup):
def test_{{ method }}_{{ path|to_snake_case }}(self, {{ client_type }}_{{service_name|to_snake_case}}) -> None:
{{ client_type }}_{{service_name|to_snake_case}}.{{ api_name|to_snake_case }}_api.{{ method }}_{{ path|to_snake_case }}()
В скобки будут подставлены переменные, такие как название method, path, client_type и тп.
А чтобы отрендерить этот шаблон, то есть собрать из него текст нужно на вход подать нужные переменные, которые мы можем спокойно взять из OpenAPI спеки.
result = env.get_template("tests.jinja2").render(
method="post",
client_type="http",
path="v1/account",
api_name="account",
service_name="dm-api-account",
)
Я люблю такие задачи на парсинг, они довольно увлекательные, понятные, но далеко не простые, потому что исходя из многообразия схем возникает многообразие ошибок))
TG-сообщество | Обучение |+2
Обратил внимание как хреново работают нейросети в РФ, вчера задали простой вопрос Алисе, Гигачату и чату Гпт, и вот результат. Притом что переубедить Алису в том что она считает не правильно так и не удалось.
Кто юзает отечественные нейросети, сталкивались с проблемами?
TG-сообщество | Обучение |
Всем привет, хочу сделать небольшой анонс, завтра у меня денюха и дата довольно приличная)
В честь этого будет подарок, завтра расскажу какой)
Моки в автотестах.
Я заметил что вам очень нравятся посты про Unit тесты, на днях я думал и нашел как можно применить подходы, которые там есть для разработки сценариев тестировщиками.
Представим, что у нас в процессе пилежки есть фича, уже есть нарезанная задача, описан контракт, используя инструментарий юнитестов мы можем начать писать тесты не дожидаясь пока фичу напилит разработка.
Допустим я знаю, что разработчик пилит новую ручку, которая будет называться get_v1_account, для нее я опишу клиент.
class AccountApi(RestClient):
def get_v1_account(self, validate_response=True, **kwargs):
"""
Get current user
:return:
"""
response = self.get(path=f"/v1/account", **kwargs)
if validate_response:
return UserDetailsEnvelope(**response.json())
return response
Как мы видим здесь мы используем RestClient, выдерну небольшой кусок из моего курса:
class RestClient:
def __init__(self, configuration: Configuration):
self.host = configuration.host
self.session = session()
self.log = structlog.get_logger(__name__).bind(service="api")
def get(self, path, **kwargs):
return self._send_request(method="POST", path=path, **kwargs)
Я скипну описание классов фасадов, и покажу очень простой пример как может выглядеть тест:
def test_get_v1_account_mock(account_api):
response = account_api.get_v1_account(validate_response=False)
Теперь давайте подумаем какие тесты мы можем написать:
1. Ну допустим любимую всеми проверку на статус код.
2. Проверку валидного ответа сервера
3. Проверку ответа сервера в случае ошибок
Это при условии, что мы узнали например у аналитика или у разработчика какие ответы он будет зашивать на какие статус коды.
Дальше все, что нам остается это замокать ответ сервера на нашей стороне, например так.
from unittest.mock import patch, MagicMock
import packages.restclient.client
@pytest.mark.parametrize("return_value, expected_status_code, expected_message", [
[MagicMock(status_code=200, json={"login": "vmenshikov"}), 200, {"login": "vmenshikov"}],
[MagicMock(status_code=400, json={"error": "Bad Request"}), 400, {"error": "Bad Request"}],
[MagicMock(status_code=500, json={"error": "Server Error"}), 500, {"error": "Server Error"}]
]
)
def test_get_v1_account_mock(
account_api,
return_value,
expected_status_code,
expected_message,
):
with patch.object(
packages.restclient.client.RestClient, "get", return_value=return_value
):
response = account_api.get_v1_account(validate_response=False)
assert response.status_code == expected_status_code
assert response.json == expected_message
В качестве объекта ответа я использовал специальный объект MagicMock, который вернет нам то, на что мы хотим заложиться, а чтобы подсунуть этот мок объект вместо ответа сервера я использую контекстный менеджер patch, который в рест клиенте будет отдавать именно мок вместо реального ответа.
Тем самым мы можем начать писать автотесты без существования самого сервера и более того даже без интернета))
А когда приложение будет задеплоено, нам останется только убрать моки))
TG-сообщество | Обучение |В пятницу как всегда ничего сложного, просто скажу по факту)
Те у кого остался доступ к REST & GRPC вчера выложил туда три урока из Advanced уж чето сильно вы просили)
+1
Приветствую, уже началась работа над курсом professional.
После этой части будем переходить к базкам.
Долго думал что первее, но логичнее и правильнее на мой взгляд, выкатить именно эту часть.
Всем привет)
Хорошая новость для тех кто уже имеет доступ к курсу Advanced, и для тех кто только планирует на него пойти)
Добавил новые уроки в 4 модуль по снятию coverage сервиса, нотификации, и работе с GitLab.
А так же я провел ребрендинг, я переехал на новый домен, который соответствует названию канала, а так же сменил дезигн))
Так что самое актуальное теперь по адресу:
https://aqa-engineer.com/index
Про асинхронный код
Часть 4
Сегодня давайте поговорим о том, какие операции можно проводить асинхронно в Python и разберем разницу между IO-bound и CPU-bound задачами.
Важно понимать, что выполнение далеко не всех задач асинхронно повысит производительность, так какие задачи стоит выполнять асинхронно?
Это IO-bound операции, например:
1. Запросы к API: Если ваш код взаимодействует с внешними сервисами (например, получение данных из API), асинхронные запросы помогут избежать блокировки основного потока.
2. Работа с файловой системой: Чтение и запись больших файлов могут занять время. Используя асинхронные операции, вы можете продолжать выполнять другие задачи, пока файл загружается или сохраняется.
3. Сетевые операции: Любые задачи, требующие взаимодействия по сети (например, скачивание изображений), отлично подходят для асинхронного выполнения.
4. Работа с базами данных: Многие библиотеки поддерживают асинхронные операции для выполнения запросов к базам данных, что позволяет эффективно использовать ресурсы.
В задачах этого типа приложение затрачивает много времени на ожидание ответа от внешних ресурсов, а не на выполнение вычислений. Асинхронный подход будет особенно эффективен в таких ситуациях, так как он позволяет выполнять другие задачи в ожидании завершения I/O операции.
CPU-bound операции:
- CPU-bound операции, в свою очередь, требуют значительных вычислительных ресурсов. К ним относятся сложные математические расчеты, обработка изображений и другие задачи, требующие мощной обработки данных. В этом случае асинхронный подход не даст желаемого результата, так как CPU не сможет работать над несколькими задачами одновременно. А так как мы с вами знаем о существовании GIL, то чтобы увеличить производительность этих задач мы можем использовать многопроцессорность.
Допустим у нас есть три функции, каждая из которых выполняют расчеты, каждый из которых выполняется три секунды например:
def math_f1():
# работает 3 секунды
def math_f2():
# работает 3 секунды
def math_f3():
# работает 3 секунды
Если мы будем выполнять их хоть в потоках, хоть асинхронно суммарная длительность их выполнения будет 9 секунд.
Однако если мы их запустим в процессах, например так:
import multiprocessing
def run_with_multiprocessing():
with multiprocessing.Pool(processes=3) as pool:
math_f1()
math_f2()
math_f3()
Суммарная длительность их выполнения будет уже зависеть от количества ядер процессора, то есть если 1 ядро то мы получим те же 9 секунд, если 2 то 6, и если 3 ядра то получим всего три секунды.
Вывод: в контексте автотестирования API, и работы с веб-приложениями, большую часть времени мы работает с IO-bound задачами, то есть дожидаемся ответов от сторонних ресурсов и здесь асинхронное программирование поможет нам удобнее работать с такими ресурсами как БД, kafka и тп. Но для CPU-bound задач, если вы вдруг тестируете, что-то связанное с расчетами, рендерингом и тп, для ускорения производительности лучше использовать мультипроцессинг.По традиции в пятницу, ничего сложного, просто мемас про дизайн системы)
Для тех кто любит поговорить напомню есть еще:
TG-сообщество
Вчера сидел и думал, как бы добавить в курс материал о том, как прикрутить coverage к Swagger.
Выше в постах вы могли увидеть пример отчёта.
Так как контрибьютить в используемую библиотеку мне лень, получится полезный урок о том, как скачать библиотеку и доработать её под свои нужды.
В общем и целом, это будет добавлено в курс Advanced, и я надеюсь, что студенты, у которых есть доступ, это обкатают.
Ну и, опять же, решил прикрутить к Advanced отправку уведомлений — получилось довольно симпатично.
Всем привет! В этом посте хочу немного отойти от предыдущих тем, чтобы разбавить контент и поговорить про юнит-тесты.
Как вы знаете, сейчас я разработчик и могу приоткрыть завесу тайны о том, как они пишутся и нужны ли они тестировщикам.
Сейчас я пишу их довольно много, и, если честно, это довольно сложно, но в целом очень интересно. Пока я не увидел необходимости их применения для обычных тестировщиков и автоматизаторов, если они не разрабатывают библиотеки. Тем не менее, меня довольно часто про них спрашивают.
Так вот, что в них проверяют? В первую очередь, они предназначены для тестирования публичных методов класса, чтобы разработчик мог рефакторить код и при этом тесты позволяли ему убедиться, что код не стал работать хуже.
Для тех, кто не в курсе: публичные методы — это методы без "поджопников", как выражается мой текущий тимлид. Методы с поджопниками, типа:
def _method_name(args): ...
или этого
def __method_name(args): ...
— это уже методы, предназначенные для внутреннего применения внутри класса, и завязываться на них никогда не стоит, потому что есть вероятность, что их могут поменять.
Теперь давайте посмотрим пример теста. Я возьму его из своего проекта. У меня есть функция, которая запускает какую-то команду из терминала с помощью модуля subprocess. Это могут быть различные команды, например, линуксовые или команды git.
def run_command(command: List[str], ignore_error: bool = False) -> str:
result = subprocess.run(args=command, text=True, capture_output=True)
print(result.stdout)
if result.returncode != 0 and not ignore_error:
print(f"{Fore.RED}Error: {result.stderr}, for command: {' '.join(command)}")
sys.exit(1)
return result.stdout.strip()
И есть другая функция, которая как раз и использует функцию run_command для выполнения git-команды:
def check_git_repository() -> None:
is_work_tree = run_command(["git", "rev-parse", "--is-inside-work-tree"], ignore_error=True)
if not is_work_tree:
print(f"{Fore.RED} Erro try again!")
sys.exit(1)
И именно эту функцию я хочу протестировать. Для начала я замокаю функцию самого модуля subprocess, чтобы я мог подсунуть вместо нее необходимый мне ответ.
@pytest.fixture
def mock_subprocess_run() -> Generator[Mock, None, None]:
with patch("subprocess.run") as mock_run:
yield mock_run
После этого я напишу сам тест, в котором с помощью объекта MagicMock подсуну в ответ то, что мне нужно, например как будто subprocess.run выполнился успешно.
def test_check_git_repository__success(mock_subprocess_run: Mock) -> None:
mock_subprocess_run.return_value = MagicMock(returncode=0, stdout="true", stderr="")
command = ["git", "rev-parse", "--is-inside-work-tree"]
check_git_repository()
mock_subprocess_run.assert_called_once_with(args=command, text=True, capture_output=True)
Далее у меня выполняется тестируемая функция check_git_repository, и с помощью своего мока я проверяю, что функция subprocess.run была выполнена успешно с переданными в нее аргументами.
Если же я захочу проверить негативный сценарий, я сделаю следующее: в качестве своего ответа в мок передам ошибку и проверю, что при таком же сценарии у меня будет выброшена ошибка SystemExit, потому что именно она у меня выбрасывается в этой строчке кода sys.exit(1) функции check_git_repository.
def test_check_git_repository__has_error(mock_subprocess_run: Mock) -> None:
mock_subprocess_run.return_value = MagicMock(returncode=1, stdout="", stderr="error")
command = ["git", "rev-parse", "--is-inside-work-tree"]
with pytest.raises(SystemExit):
check_git_repository()
mock_subprocess_run.assert_called_once_with(args=command, text=True, capture_output=True)
Вот как то так, в целом самое сложное - это правильно замокать нужные функции и методы, а чем сложнее класс тем сложнее его тестировать и мокать, в целом это довольно увлекательно, но пока я не вижу применения этого скила автотестировщику, может вы накинете?20 минутное не буду выкладывать, но это тоже покажу, уж больно нравится)
Всем привет))
Сегодня пришло видео со свадьбы которое мы ждали 4 месяца)
Не смотря на то, что блог у меня технический, иногда хочется поделиться и личным, поэтому приятного просмотра)
По традиции в пятницу без сложных постов, а просто пару уроков программирования)
Всем привет, сегодня нет времени писать умные посты, просто хочу поделиться библиотекой, которая позволяет снять покрытие сервиса тестами.
Ее можно использовать или через предлагаемый автором listener, что по сути является оберткой над requests, либо если чуть полазить в коде, можно использовать RequestSchemaHandler просто передав туда нужные аргументы, например так.
def _send_requests(self, method, path, **kwargs):
full_url = self.host + path
rest_response = self.session.request(method=method, url=full_url, **kwargs)
url = URI(
host=self.host,
base_path="",
unformatted_path=path,
uri_params=kwargs.get("params"),
)
RequestSchemaHandler(
url, method.lower(), rest_response, kwargs
).write_schema()
return rest_response
Для работы необходима установленная на комп или в контейнер JAVA
Endi mavjud! Telegram Tadqiqoti 2025 — yilning asosiy insaytlari 
