✅ Решения заданий

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

⚡ Ключевые паттерны решений

  • Фильтрация активных: [c for c in resp.json() if c["is_active"]]
  • Негативный тест: assert resp.status_code == 422 при пустом json={}
  • Рост списка: GET → len → POST → GET → assert new == old + 1
  • reqres.in: user = data["data"] → assert поля

Решение 1: Список активных компаний

# test_companies.py
import requests

def test_get_active_companies():
    url = "http://5.101.50.27:8000/company/list"

    # 1. Отправляем GET-запрос
    response = requests.get(url)

    # 2. Проверяем успешный статус
    assert response.status_code == 200, f"Ошибка: статус {response.status_code}"

    # 3. Разбираем JSON-ответ
    companies = response.json()

    # 4. Фильтруем только активные компании
    active_companies = [company for company in companies if company["is_active"]]

    # 5. Проверяем количество активных компаний
    assert len(active_companies) >= 3, \
        f"Ожидалось >=3 активных компаний, но найдено {len(active_companies)}"

    print(f"Тест пройден! Найдено {len(active_companies)} активных компаний.")

Объяснение

  • response.json() — разбирает JSON-массив в Python list
  • List comprehension [c for c in companies if c["is_active"]] — фильтрует только активные
  • len(active_companies) >= 3 — гибкое условие (не хардкодим точное число)

Решение 2: Негативный тест — пустое тело

# test_companies.py
import requests

BASE_URL = "http://5.101.50.27:8000"

def test_create_company_empty_body():
    """Отправка пустого JSON должна вернуть 422"""
    resp = requests.post(BASE_URL + "/company/create", json={})
    response = resp.json()

    assert resp.status_code == 422, \
        f"Ожидался 422, получен {resp.status_code}"
    assert response["detail"][0]["msg"] == "Field required"

Объяснение

  • Сервер требует поле name — без него возвращает 422 Unprocessable Entity
  • Структура ошибки Pydantic: detail[0]["msg"] содержит текст "Field required"
  • Негативные тесты важны: они проверяют, что API корректно отклоняет некорректные запросы

Решение 3: Рост списка компаний

# test_companies.py
import requests

BASE_URL = "http://5.101.50.27:8000"

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

    # 1. Получаем текущий список компаний
    resp = requests.get(BASE_URL + "/company/list")
    assert resp.status_code == 200, f"Ошибка: {resp.status_code}"
    companies_before = resp.json()
    initial_count = len(companies_before)

    # 2. Создаём новую компанию
    new_company = {
        "name": "Test Company",
        "description": "Automated test creation"
    }
    resp_create = requests.post(BASE_URL + "/company/create", json=new_company)
    assert resp_create.status_code == 201, f"Ошибка создания: {resp_create.status_code}"

    # 3. Повторно получаем список компаний
    resp_after = requests.get(BASE_URL + "/company/list")
    assert resp_after.status_code == 200, f"Ошибка: {resp_after.status_code}"
    companies_after = resp_after.json()
    final_count = len(companies_after)

    # 4. Проверяем: стало на 1 больше
    assert final_count == initial_count + 1, \
        f"Ожидалось {initial_count + 1} компаний, найдено {final_count}"

    print(f"Тест пройден: до {initial_count}, после {final_count}")

Объяснение

  • Не хардкодим ID компании — создаём новую и сразу проверяем изменение списка
  • Три запроса: GET → POST (create) → GET — стандартный паттерн для тестов мутаций
  • assert final_count == initial_count + 1 — точная проверка на +1, не просто "> 0"

Решение 4: GET к reqres.in

⚠️ С 2024 года reqres.in требует бесплатный ключ x-api-key: reqres-free-v1 — без него ответ 401. Передаём заголовок HEADERS в каждый запрос.
# test_reqres.py
import requests
import pytest

BASE_URL = "https://reqres.in/api/users"
HEADERS = {"x-api-key": "reqres-free-v1"}  # бесплатный ключ reqres.in (с 2024)

@pytest.mark.parametrize("user_id, expected_first_name, expected_last_name, expected_email", [
    (2, "Janet", "Weaver", "janet.weaver@reqres.in"),
])
def test_get_existing_user(user_id, expected_first_name, expected_last_name, expected_email):
    """Тест: получение существующего пользователя"""
    response = requests.get(f"{BASE_URL}/{user_id}", headers=HEADERS)

    # Проверяем статус
    assert response.status_code == 200, f"Ожидался статус 200, получен {response.status_code}"

    data = response.json()

    # Проверяем структуру ответа
    assert "data" in data, "Ответ API не содержит ключ 'data'"

    user = data["data"]

    # Проверяем данные пользователя
    assert user["id"] == user_id
    assert user["first_name"] == expected_first_name, \
        f"Ожидалось '{expected_first_name}', получено '{user['first_name']}'"
    assert user["last_name"] == expected_last_name
    assert user["email"] == expected_email


def test_get_non_existing_user():
    """Тест: несуществующий пользователь → 404"""
    user_id = 23
    response = requests.get(f"{BASE_URL}/{user_id}", headers=HEADERS)

    assert response.status_code == 404, f"Ожидался статус 404, получен {response.status_code}"
    assert response.text == "{}", f"Ожидался пустой JSON, получен {response.text}"

Объяснение

  • @pytest.mark.parametrize — позволяет легко добавлять тест-кейсы без дублирования кода
  • data["data"] — ответ reqres.in вложен в ключ "data", нужно «пробурить» структуру
  • Тест на несуществующего пользователя — проверяем 404 и пустое тело {}