✅ Решения: ответы на вопросы самопроверки
⚡ Ключевые ответы
- resp.json() — dict/list; resp.text — всегда строка
- Статус создания — 201 Created
- CompanyApi нужен для инкапсуляции: тесты не дублируют requests-вызовы
- Хардкод ID плохо — база может быть пустой; ID берём из ответа на создание
Блок 1: Основы HTTP и requests
Вопрос 1. Клиент-серверная архитектура
Клиент — устройство или программа, которая отправляет запросы: браузер, мобильное приложение, Python-скрипт с requests. Сервер — принимает запросы, обрабатывает и возвращает ответы. Клиентов может быть много, сервер — один. Пример: ваш браузер (клиент) → запрос к google.com → сервер Google → HTML-ответ.
Вопрос 2. HTTP vs HTTPS
HTTP — данные передаются открыто, могут быть перехвачены. HTTPS — добавляет шифрование TLS: данные между клиентом и сервером зашифрованы. HTTPS обязателен при передаче паролей, платёжных данных, персональных данных.
Вопрос 3. HTTP-методы
| Метод | Что делает | Пример в XClients |
|---|---|---|
| GET | Получение данных | GET /company/list |
| POST | Создание ресурса / авторизация | POST /auth/login, POST /company/create |
| PUT | Полное обновление | Полная замена записи |
| PATCH | Частичное обновление | PATCH /company/update/{id} |
| DELETE | Удаление | DELETE /company/{id} |
Вопрос 4. Состав запроса и ответа
Запрос: URL, метод, заголовки (Headers), тело (Body — JSON или form-data).
Ответ: статус-код (200, 201, 404…), заголовки, тело (JSON, HTML или пустое).
Блок 2: Библиотека requests
Вопрос 5. Установка
pip install requests
pip show requests # проверка: версия, путь установки
Вопрос 6. Атрибуты Response
resp.status_code— числовой HTTP-статус (200, 404…)resp.json()— парсит JSON → dict/listresp.text— тело ответа как строка (всегда)resp.headers— словарь заголовков ответаresp.ok— True если status_code < 400
Вопрос 7. resp.text vs resp.json()
resp = requests.get("https://jsonplaceholder.typicode.com/posts/1")
print(type(resp.text)) # <class 'str'> — всегда строка
print(type(resp.json())) # <class 'dict'> — Python-объект
# resp.text нельзя: resp.text["title"] -> TypeError
# resp.json() можно: resp.json()["title"] -> строка
Если ответ не JSON и вызвать resp.json() — будет json.JSONDecodeError.
Вопрос 8. Передача JSON в POST
# Правильно: json= (requests автоматически ставит Content-Type: application/json)
resp = requests.post(url, json={"name": "test"})
# Устаревший способ: data= (передаёт как form-data, не JSON)
# requests.post(url, data={"name": "test"}) -- НЕ используй для JSON API
Блок 3: Работа с JSON
Вопрос 9. json.loads()
import json
# JSON-объект -> dict
obj_str = '{"id": 1, "name": "Test"}'
result = json.loads(obj_str) # dict: {"id": 1, "name": "Test"}
# JSON-массив -> list
arr_str = '[{"id": 1}, {"id": 2}]'
result = json.loads(arr_str) # list: [{"id": 1}, {"id": 2}]
Вопрос 10. Частые ошибки JSON + обработка JSONDecodeError
import json
try:
bad = "{ 'id': 1 }" # одинарные кавычки — неправильный JSON
data = json.loads(bad)
except json.JSONDecodeError as e:
print(f"Ошибка разбора JSON: {e}")
Частые ошибки: одинарные кавычки вместо двойных; лишняя запятая перед }; True/None вместо true/null.
Вопрос 11. JSON vs Python
| JSON | Python |
|---|---|
true | True |
false | False |
null | None |
| только двойные кавычки | одинарные или двойные |
Блок 4: Класс CompanyApi
Вопрос 12. Зачем класс?
Без класса: дублирование requests.get() / requests.post() в каждом тесте; проверка статуса в каждом тесте; при смене URL — ручное исправление везде.
С классом: запросы в одном месте; проверки статуса внутри методов; тест вызывает только api.get_company_list() — не знает деталей HTTP; легко переиспользовать.
Вопрос 13. Методы CompanyApi
get_company_list()— GET /company/list → list[dict]get_token(user, password)— POST /auth/login → str (токен)create_company(name, description)— POST /company/create → dictget_company(company_id)— GET /company/{id} → dictedit_company(id, name, descr)— PATCH /company/update/{id} → dictdelete_company(company_id)— DELETE /company/{id} → dictset_active_state(id, is_active)— PATCH /company/status_update/{id} → dict
Вопрос 14. Почему нельзя хардкод-ID?
База данных может быть очищена — ID 7 не существует. IDs меняются с каждым запуском тестов. Надёжный тест: создать компанию → взять ID из ответа → использовать этот ID.
Блок 5: Решения практических задач
Задача 1: test_simple_req()
# 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")
response_body = resp.json()
first_company = response_body[0]
assert resp.status_code == 200
assert resp.headers["Content-Type"] == "application/json"
assert first_company["name"] == "QA Студия 'ТестировщикЪ'"
Задача 2: test_auth()
# test_x_clients.py
import requests
BASE_URL = "http://5.101.50.27:8000"
def test_auth():
creds = {
"username": "harrypotter",
"password": "expelliarmus"
}
resp = requests.post(BASE_URL + "/auth/login", json=creds)
assert resp.status_code == 200
assert resp.json()["user_token"] is not None
Задача 3: тест с CompanyApi
# test_x_clients.py
from company_api import CompanyApi
BASE_URL = "http://5.101.50.27:8000"
def test_get_one_company():
api = CompanyApi(BASE_URL)
name = "PyCharm"
descr = "IDE"
result = api.create_company(name, descr)
new_id = result["id"]
new_company = api.get_company(new_id)
assert new_company["name"] == name
assert new_company["description"] == descr
assert new_company["is_active"] is True