🐛 Типичные ошибки: requests и API
⚡ Топ-3 ошибки
- resp.text["key"] — нельзя: text — строка. Используй
resp.json()["key"] - Нет проверки status_code — тест читает тело ответа при ошибке 500 и падает с непонятным сообщением
- Хардкод URL в каждом тесте — при смене сервера нужно менять везде
Ошибка 1: resp.text вместо resp.json()
Симптом:
TypeError: string indices must be integers при обращении resp.text["name"]
# Плохо: resp.text — строка, нельзя обратиться к полю как к dict
resp = requests.get(BASE_URL + "/company/list")
first_company = resp.text[0] # ошибка: [0] — первый символ строки, не объект
name = resp.text["name"] # TypeError
# Хорошо: resp.json() возвращает list или dict
resp = requests.get(BASE_URL + "/company/list")
body = resp.json() # list[dict]
first_company = body[0] # первый элемент списка
name = first_company["name"] # корректно
Ошибка 2: нет проверки status_code
Симптом: тест падает с
KeyError или JSONDecodeError, хотя проблема — 500 Internal Server Error от сервера.
# Плохо: сразу читаем тело без проверки статуса
resp = requests.post(BASE_URL + "/company/create", json={"name": "Test"})
new_id = resp.json()["id"] # KeyError если сервер вернул 500 с пустым телом
# Хорошо: сначала проверяем статус
resp = requests.post(BASE_URL + "/company/create", json={"name": "Test"})
assert resp.status_code == 201, f"Ожидался 201, получен {resp.status_code}: {resp.text}"
new_id = resp.json()["id"] # безопасно
Ошибка 3: хардкод URL в каждом тесте
Симптом: при смене сервера нужно исправлять URL в десятках мест.
# Плохо: хардкод в каждом тесте
def test_one():
requests.get("http://5.101.50.27:8000/company/list")
def test_two():
requests.post("http://5.101.50.27:8000/auth/login", json={...})
# Хорошо: одна переменная или класс
BASE_URL = "http://5.101.50.27:8000"
def test_one():
requests.get(BASE_URL + "/company/list")
def test_two():
requests.post(BASE_URL + "/auth/login", json={...})
Ошибка 4: одинарные кавычки в JSON
Симптом:
json.JSONDecodeError: Expecting property name enclosed in double quotes
# Плохо: одинарные кавычки — это Python dict, не JSON
bad_json = "{ 'id': 111, 'name': 'Test' }"
data = json.loads(bad_json) # JSONDecodeError
# Хорошо: JSON требует двойные кавычки
good_json = '{ "id": 111, "name": "Test" }'
data = json.loads(good_json) # работает
# Ещё лучше: не писать JSON вручную — используй dict Python, requests сам сериализует
requests.post(url, json={"id": 111, "name": "Test"})
Ошибка 5: хардкод ID компании в тесте
Симптом: тест падает с 404 или данными другой компании — ID поменялся или компании нет.
# Плохо: предполагаем, что компания с ID 7 существует
def test_get_company():
resp = requests.get(BASE_URL + "/company/7") # может не быть!
assert resp.status_code == 200
# Хорошо: создаём компанию, берём ID из ответа
def test_get_company():
api = CompanyApi(BASE_URL)
result = api.create_company("Temp", "test")
company_id = result["id"] # динамический ID
company = api.get_company(company_id)
assert company["name"] == "Temp"
Ошибка 6: data= вместо json= для JSON-запроса
Симптом: сервер возвращает 422 Unprocessable Entity или 400 Bad Request — данные пришли как form-data, не JSON.
# Плохо: data= передаёт как application/x-www-form-urlencoded
resp = requests.post(url, data={"name": "Test"}) # не JSON!
# Хорошо: json= автоматически устанавливает Content-Type: application/json
resp = requests.post(url, json={"name": "Test"})
Ошибка 7: вызов resp.json() на не-JSON ответе
Симптом:
requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1
# Плохо: пытаемся распарсить HTML как JSON
resp = requests.get("https://example.com")
data = resp.json() # JSONDecodeError — сервер вернул HTML
# Хорошо: сначала проверяем Content-Type или используем resp.text
resp = requests.get("https://example.com")
if "application/json" in resp.headers.get("Content-Type", ""):
data = resp.json()
else:
print(resp.text) # HTML или text
Ошибка 8: assert внутри метода класса с неинформативным сообщением
Симптом: тест падает с
AssertionError без объяснения — что именно пошло не так.
# Плохо: нет сообщения об ошибке
def create_company(self, name):
resp = requests.post(self.url + "/company/create", json={"name": name})
assert resp.status_code == 201
# Хорошо: информативное сообщение с фактическим статусом
def create_company(self, name):
resp = requests.post(self.url + "/company/create", json={"name": name})
assert resp.status_code == 201, \
f"Ожидался 201, получен {resp.status_code}. Тело: {resp.text}"