Тестирование API
Полное руководство по тестированию API в Playwright Python: методы запросов, работа с ответами, изолированный контекст API и примеры CRUD-операций.
Введение в тестирование API
Playwright предоставляет встроенные возможности для тестирования REST API через объекты APIRequestContext. Вы можете выполнять HTTP-запросы напрямую, без использования браузера, что позволяет тестировать API как отдельно, так и в сочетании с UI-тестами. API-запросы можно выполнять через page.request, context.request или изолированный APIRequestContext.
Важно: API-тесты в Playwright выполняются в том же контексте, что и браузерные тесты, что упрощает передачу кук, заголовков аутентификации и других данных между UI и API.
Способы отправки запросов
page.request — запросы от лица страницы
Объект page.request автоматически использует куки и заголовки текущей страницы. Это удобно для выполнения API-запросов в контексте авторизованного пользователя.
response = page.request.get("https://api.example.com/users") response = page.request.post("https://api.example.com/users", json={"name": "Иван"})
context.request — запросы от лица контекста
Объект context.request использует куки и настройки конкретного браузерного контекста.
response = context.request.get("https://api.example.com/data") response = context.request.post("https://api.example.com/data", data="raw body content")
APIRequestContext — изолированный контекст API
Для полной изоляции можно создать отдельный контекст API через request.new_context(). Это полезно, когда нужно отправить запрос без браузерного контекста или с собственными настройками аутентификации.
from playwright.sync_api import sync_playwright with sync_playwright() as p: api_context = p.request.new_context( base_url="https://api.example.com", extra_headers={"X-API-Key": "my-secret-key"} ) response = api_context.get("/users") print(response.json())
HTTP-методы
request.get() — GET-запрос
# Простой GET-запрос response = page.request.get("https://api.example.com/users") # GET с параметрами запроса response = page.request.get("https://api.example.com/users", params={ "page": 1, "limit": 10, "sort": "name" }) # GET с заголовками response = page.request.get( "https://api.example.com/protected", headers={"Authorization": "Bearer token123"} )
request.post() — POST-запрос
# POST с JSON-телом response = page.request.post("https://api.example.com/users", json={ "name": "Иван Петров", "email": "ivan@example.com", "role": "admin" }) # POST с form-данными response = page.request.post("https://api.example.com/login", form={ "username": "admin", "password": "secret" }) # POST с multipart/form-data (загрузка файла) response = page.request.post("https://api.example.com/upload", multipart={ "file": {"name": "report.pdf", "mimeType": "application/pdf", "buffer": b"PDF content"} }) # POST с raw-телом response = page.request.post("https://api.example.com/data", data="raw string body")
request.put() — PUT-запрос
# Обновление ресурса response = page.request.put("https://api.example.com/users/42", json={ "name": "Иван Петров (обновлено)", "email": "ivan.new@example.com" }) # PUT с заголовками response = page.request.put( "https://api.example.com/users/42", data='{"name": "Обновление"}', headers={"Content-Type": "application/json"} )
request.patch() — PATCH-запрос
# Частичное обновление ресурса response = page.request.patch("https://api.example.com/users/42", json={ "email": "updated@example.com" })
request.delete() — DELETE-запрос
# Удаление ресурса response = page.request.delete("https://api.example.com/users/42") # DELETE с телом запроса response = page.request.delete("https://api.example.com/users/batch", json={ "ids": [1, 2, 3] })
request.fetch() — универсальный метод
Метод fetch() позволяет выполнить запрос с полным контролем над всеми параметрами. Это универсальная замена для всех отдельных HTTP-методов.
# GET-запрос через fetch response = page.request.fetch("https://api.example.com/users") # POST с JSON через fetch response = page.request.fetch( "https://api.example.com/users", method="POST", headers={"Content-Type": "application/json"}, json={"name": "Новый пользователь"} ) # Полный кастомный запрос через fetch response = page.request.fetch( "https://api.example.com/data", method="PUT", headers={"Authorization": "Bearer token", "X-Custom": "value"}, params={"version": "2"}, data='{"key": "value"}', timeout=30000 )
Работа с ответом (APIResponse)
Объект APIResponse предоставляет множество методов для проверки и разбора ответа сервера.
Статус и заголовки
response = page.request.get("https://api.example.com/users") # Код статуса print(response.status) # 200 print(response.ok) # True (200-299) print(response.status_text) # "OK" # Проверка статуса assert response.status == 200 assert response.ok assert response.status == 201 # для создания ресурса assert response.status == 204 # для успешного удаления # Заголовки ответа print(response.headers) print(response.headers["content-type"]) print(response.headers.get("x-request-id")) print(response.headers_array()) # все заголовки с дубликатами
Тело ответа
# JSON (автоматический парсинг) data = response.json() print(data["name"]) print(data["items"][0]) # Текст text = response.text() print(text) # Бинарные данные (байты) body = response.body() with open("downloaded.pdf", "wb") as f: f.write(body)
Дополнительные методы APIResponse
# URL после возможных редиректов print(response.url) # Размер тела в байтах print(response.body_size) # int # Проверка, завершён ли ответ (все данные получены) assert response.finished() # True после полной загрузки тела
Безопасность и TLS
response.security_details() — информация о TLS
Возвращает информацию о TLS-соединении: версия протокола, шифр, сертификат.
response = page.request.get("https://api.example.com/users") security = response.security_details() if security: print(security["protocol"]) # "TLS 1.3" print(security["cipher"]) # "AES-256-GCM" print(security["subjectName"]) # "api.example.com" print(security["issuer"]) # "Let's Encrypt" print(security["validFrom"]) # timestamp print(security["validTo"]) # timestamp
response.server_addr() — адрес сервера
Возвращает IP-адрес и порт сервера, который обработал запрос.
response = page.request.get("https://api.example.com/users") addr = response.server_addr() if addr: print(addr["ipAddress"]) # "93.184.216.34" print(addr["port"]) # 443
Параметры запроса: заголовки, куки и query-параметры
# Отправка кастомных заголовков response = page.request.get( "https://api.example.com/users", headers={ "Authorization": "Bearer eyJhbGciOiJIUzI1Ni...", "X-Request-ID": "abc-123", "Accept-Language": "ru-RU" } ) # Query-параметры (URL-параметры) response = page.request.get("https://api.example.com/search", params={ "q": "playwright", "page": 2, "sort": "desc", "filter": "active" }) # Отправка кук response = page.request.get( "https://api.example.com/profile", headers={"Cookie": "session_id=abc123; theme=dark"} )
request.storage_state() — состояние для аутентификации
Позволяет получить куки, localStorage и sessionStorage после выполнения запросов. Это полезно для сохранения состояния аутентификации и повторного использования в других тестах.
# Выполнение входа и сохранение состояния response = page.request.post("https://example.com/api/login", json={ "username": "admin", "password": "password123" }) assert response.ok # Сохранение состояния (куки, токены) state = page.request.storage_state() with open("auth-state.json", "w") as f: f.write("json.dumps(state)") # Использование сохранённого состояния в новом контексте context = browser.new_context(storage_state="auth-state.json") page = context.new_page() # Теперь пользователь уже авторизован page.goto("https://example.com/dashboard")
Совет: Используйте storage_state() для ускорения тестов — выполняйте вход один раз через API (это быстрее, чем UI), сохраняйте состояние и переиспользуйте его в последующих тестах. Это также позволяет избежать многократного ввода логина/пароля.
Полный пример CRUD-теста API
from playwright.sync_api import sync_playwright class TestUsersAPI: base_url = "https://api.example.com" created_user_id = None def setup_method(self): self.playwright = sync_playwright().start() self.api_context = self.playwright.request.new_context( base_url=self.base_url, extra_headers={"Authorization": "Bearer test-token"} ) def teardown_method(self): self.api_context.dispose() self.playwright.stop() def test_01_create_user(self): # CREATE (POST) — создание нового пользователя response = self.api_context.post("/users", json={ "name": "Иван Петров", "email": "ivan@example.com", "role": "user" }) assert response.status == 201 data = response.json() self.created_user_id = data["id"] assert data["name"] == "Иван Петров" assert data["email"] == "ivan@example.com" def test_02_get_user(self): # READ (GET) — получение созданного пользователя assert self.created_user_id is not None response = self.api_context.get(f"/users/{self.created_user_id}") assert response.status == 200 assert response.ok data = response.json() assert data["id"] == self.created_user_id assert data["name"] == "Иван Петров" def test_03_update_user(self): # UPDATE (PUT) — обновление пользователя response = self.api_context.put(f"/users/{self.created_user_id}", json={ "name": "Иван Петров (обновлено)", "email": "ivan.updated@example.com" }) assert response.status == 200 data = response.json() assert data["email"] == "ivan.updated@example.com" def test_04_list_users(self): # LIST (GET) — получение списка пользователей response = self.api_context.get("/users", params={ "page": 1, "limit": 10 }) assert response.status == 200 data = response.json() assert "items" in data assert len(data["items"]) > 0 def test_05_delete_user(self): # DELETE — удаление пользователя response = self.api_context.delete(f"/users/{self.created_user_id}") assert response.status == 204 # Проверка, что пользователь действительно удалён response = self.api_context.get(f"/users/{self.created_user_id}") assert response.status == 404
Тестирование API в сочетании с UI
Одна из сильных сторон Playwright — возможность комбинировать API-запросы с UI-тестами. Например, можно подготовить данные через API, а затем проверить их отображение в браузере.
def test_create_user_via_api_check_ui(page, context): # Шаг 1: Создание пользователя через API api_context = context.request response = api_context.post("https://example.com/api/users", json={ "name": "Тестовый пользователь", "email": "test@example.com" }) assert response.ok user_id = response.json()["id"] # Шаг 2: Проверка в UI, что пользователь отображается page.goto("https://example.com/users") expect(page.get_by_text("Тестовый пользователь")).to_be_visible() expect(page.get_by_text("test@example.com")).to_be_visible() # Шаг 3: Очистка — удаление пользователя через API api_context.delete(f"https://example.com/api/users/{user_id}")
Совет: Используйте API для подготовки данных (сев-ап) и очистки (тир-даун) в тестах. Это быстрее и надёжнее, чем создавать данные через UI. Кроме того, API-запросы не зависят от состояния интерфейса.
Заключение
Тестирование API в Playwright Python — это полноценный инструмент, который позволяет:
- Выполнять все стандартные HTTP-методы (GET, POST, PUT, PATCH, DELETE)
- Работать с различными форматами тела запроса (JSON, form, multipart, raw)
- Проверять статусы, заголовки и тело ответа
- Получать информацию о TLS-соединении и адресе сервера
- Использовать изолированный API-контекст или контекст страницы/браузера
- Сохранять состояние аутентификации через
storage_state() - Комбинировать API-запросы с UI-тестами для эффективного тестирования