📖 Теория: Summary session 7 — Requests

← К оглавлению урока

⚡ Requests за 2 минуты

requests — библиотека для HTTP-запросов. resp = requests.get(url) → resp.status_code, resp.json().

  • requests.get(url) / requests.post(url, json=data) / requests.patch() / requests.delete()
  • resp.status_code — 200 OK, 201 Created, 404 Not Found, 500 Server Error
  • resp.json() — dict или list; resp.text — строка; resp.headers — словарь заголовков
  • В тесте: assert resp.status_code == 200 + assert resp.json()["field"] == value

Клиент-серверная архитектура

Из лекции: клиент — устройство или программа, которая отправляет запросы (браузер, мобильное приложение, Python-скрипт). Сервер — принимает запросы, обрабатывает и возвращает ответы. Клиентов может быть много, сервер — один.

ПротоколРасшифровкаОсобенность
HTTPHyperText Transfer ProtocolДанные передаются открыто
HTTPSHTTP SecureДанные зашифрованы (TLS); обязателен для паролей, платёжных данных

Состав HTTP-запроса

Из лекции: HTTP-запрос состоит из четырёх частей:

  • URL — адрес ресурса: http://5.101.50.27:8000/company/list
  • Метод — что делать с ресурсом (GET, POST, PATCH, DELETE…)
  • Заголовки (Headers) — дополнительные параметры: Content-Type, Authorization, Accept
  • Тело (Body) — данные, которые передаются серверу (JSON, форма); у GET-запросов обычно пустое

HTTP-методы (из лекции)

МетодОписаниеПример
GETПолучение данных (без изменений)Список компаний, профиль пользователя
POSTСоздание нового ресурсаСоздать компанию, авторизоваться
PUTПолное обновление ресурсаЗаменить все поля пользователя
PATCHЧастичное обновлениеИзменить только имя или статус
DELETEУдаление ресурсаУдалить компанию по ID
Важно из лекции: разработчики сами определяют, какой метод использовать. Иногда это противоречит стандартам. Например, во второй курсовой работе удаление личного события выполняется через POST-запрос, а не DELETE. Всегда проверяй API-документацию.

Состав HTTP-ответа

  • Статус-код — трёхзначное число: 200 OK, 201 Created, 404 Not Found, 500 Internal Server Error
  • ЗаголовкиContent-Type: application/json и другие
  • Тело (Body) — данные от сервера (JSON, HTML); может отсутствовать
КатегорияОписаниеПримеры
1xxИнформационные100, 101
2xxУспешные запросы200 OK, 201 Created
3xxПеренаправления301 Moved Permanently, 304 Not Modified
4xxОшибки клиента400 Bad Request, 404 Not Found
5xxОшибки сервера500 Internal Server Error, 503 Unavailable

Библиотека requests: установка и основы

Из лекции: requests — популярная Python-библиотека для отправки HTTP-запросов. Упрощает работу с API.

# Установка (PowerShell/терминал)
pip install requests

# Проверка установки
pip show requests

# Установка конкретной версии
pip install requests==2.31.0

# Обновление до последней версии
pip install --upgrade requests

Базовые методы

# test_api.py
import requests

BASE_URL = "http://5.101.50.27:8000"

# GET — получение данных
resp = requests.get(BASE_URL + "/company/list")

# POST — создание / авторизация (данные передаём как json=)
resp = requests.post(BASE_URL + "/auth/login", json={"username": "harrypotter", "password": "expelliarmus"})

# PATCH — частичное обновление (с токеном в query-параметре)
resp = requests.patch(BASE_URL + f"/company/update/{company_id}?client_token={token}", json={"name": "New Name"})

# DELETE — удаление
resp = requests.delete(BASE_URL + f"/company/{company_id}?client_token={token}")

Объект Response

Из лекции: каждый метод requests возвращает объект Response с атрибутами:

Атрибут / МетодЧто возвращаетКогда использовать
resp.status_codeint: 200, 201, 404…Всегда проверяем первым
resp.json()dict или list (Python-объект)Когда сервер отвечает JSON
resp.textstr — тело ответа как строкаОтладка, не-JSON ответы
resp.headersdict заголовков ответаПроверить Content-Type и другие
resp.urlstr — финальный URLОтладка редиректов
resp.text vs resp.json(): resp.text всегда возвращает строку — даже если содержимое JSON. Обращаться к полям через data["key"] нельзя. resp.json() автоматически парсит JSON в dict/list — с ним работаешь как со словарём.

Работа с JSON в Python

Из лекции: JSON и Python очень похожи, но есть отличия:

JSONPython
true / falseTrue / False
nullNone
Строки только в двойных кавычках "key"Строки в одинарных или двойных
# test_json.py
import json

company_json = """
{
    "id": 111,
    "isActive": true,
    "name": "QA Studio"
}
"""

def test_parse_json():
    company = json.loads(company_json)  # строка JSON -> dict Python
    assert company["id"] == 111
    assert company["isActive"] is True  # JSON true -> Python True
Частые ошибки в JSON (из лекции):
  • Одинарные кавычки 'key' вместо двойных "key" — вызывают json.JSONDecodeError
  • Отсутствие запятой между элементами
  • Лишняя запятая перед } или ]
  • Использование True/None вместо true/null при ручном написании JSON

XClients API: практика из лекции

Из лекции: XClients — сервис записи на приём к специалистам. Базовый URL: http://5.101.50.27:8000. Модули: auth, company, employee, superuser.

Сценарий из лекции (шаги по порядку)

  1. GET /company/list — получить список компаний
  2. POST /auth/login — авторизоваться, получить user_token
  3. POST /company/create — создать компанию
  4. GET /company/list — убедиться, что компания появилась
  5. DELETE /company/{id}?client_token=... — удалить компанию
  6. GET /company/list — убедиться, что компания исчезла
# test_x_clients.py
import requests

BASE_URL = "http://5.101.50.27:8000"

def test_simple_req():
    resp = requests.get(BASE_URL + "/company/list")
    assert resp.status_code == 200
    assert resp.headers["Content-Type"] == "application/json"
    response_body = resp.json()
    first_company = response_body[0]
    assert first_company["name"] == "QA Студия 'ТестировщикЪ'"

Класс CompanyApi

Из лекции: выносим логику запросов в отдельный класс — тесты становятся чище, API-вызовы переиспользуются, при изменении сервера правим только класс.

# company_api.py
import requests


class CompanyApi:
    """Класс для взаимодействия с API компаний."""

    def __init__(self, url):
        self.url = url

    def get_company_list(self):
        resp = requests.get(self.url + "/company/list")
        assert resp.status_code == 200, f"Ожидался 200, получен {resp.status_code}"
        return resp.json()

    def get_token(self, user, password):
        creds = {"username": user, "password": password}
        resp = requests.post(self.url + "/auth/login", json=creds)
        assert resp.status_code == 200, f"Ожидался 200, получен {resp.status_code}"
        return resp.json()["user_token"]

    def create_company(self, name, description=""):
        company = {"name": name, "description": description}
        resp = requests.post(self.url + "/company/create", json=company)
        assert resp.status_code == 201, f"Ожидался 201, получен {resp.status_code}"
        return resp.json()

    def get_company(self, company_id):
        resp = requests.get(self.url + f"/company/{company_id}")
        assert resp.status_code == 200, f"Ожидался 200, получен {resp.status_code}"
        return resp.json()

    def edit_company(self, company_id, new_name, new_descr):
        token = self.get_token("harrypotter", "expelliarmus")
        url = f"{self.url}/company/update/{company_id}?client_token={token}"
        resp = requests.patch(url, json={"name": new_name, "description": new_descr})
        assert resp.status_code == 200, f"Ожидался 200, получен {resp.status_code}"
        return resp.json()

    def delete_company(self, company_id):
        token = self.get_token("harrypotter", "expelliarmus")
        url = f"{self.url}/company/{company_id}?client_token={token}"
        resp = requests.delete(url)
        assert resp.status_code == 200, f"Ожидался 200, получен {resp.status_code}"
        return resp.json()

Тест с использованием CompanyApi

# test_x_clients.py
from company_api import CompanyApi

BASE_URL = "http://5.101.50.27:8000"


def test_create_company_increases_count():
    """Тест: создание компании увеличивает список на 1."""
    api = CompanyApi(BASE_URL)

    companies_before = api.get_company_list()
    initial_count = len(companies_before)

    api.create_company(name="Test Company", description="Automated test")

    companies_after = api.get_company_list()
    assert len(companies_after) == initial_count + 1
Преимущества класса (из лекции):
  • Нет дублирования requests.get() / requests.post() в каждом тесте
  • Проверки статус-кодов — внутри класса, тесты чище
  • Если API изменится — правим только CompanyApi, тесты не трогаем
  • Легко переиспользовать в разных тестах

Итог: блок API-тестирования — начало (уроки 13–14)

УрокТемаКлючевые концепции
13Введение в RequestsHTTP, requests, Response, json(), CompanyApi
14Summary 7 (этот урок)Повторение: requests, JSON, CompanyApi, EmployeeApi (ДЗ)
15Автоматизация API-тестовpytest-фикстуры, параметризация, схемы ответов
16Summary 8Итоговое повторение курса