Python · Тестирование с pytest · Продвинутый
Интеграционное тестирование
Тестируйте несколько компонентов вместе во взаимодействии с реальными сервисами.
Краткое введение в тему и пояснения перед упражнениями (упражнения ниже):
Интеграционные тесты на практике: потоки аутентификации и общие сессии
#Справочная карточка: интеграционное тестирование
#Упражнения:
Интеграционный поток регистрации и входа
## test_register_flow.py
import time
import requests
BASE_URL = 'https://apilearn.tukas.dev'
def test_register_and_login():
username = f'pytest_{int(time.time())}'
# Шаг 1: регистрация
reg = requests.post(f'{BASE_URL}/api/users/register/', json={
'username': username,
'email': f'{username}@example.com',
'password': ...,
'first_name': 'Test',
'last_name': 'User',
})
assert reg.status_code in (200, 201)
# Шаг 2: вход
auth = requests.post(f'{BASE_URL}/api/auth/token/', json={
'username': username,
'password': ...,
})
assert auth.status_code == 200
token = ...
# Шаг 3: получить профиль
profile = requests.get(
f'{BASE_URL}/api/users/profile/',
headers={'Authorization': f'Token {token}'},
)
assert profile.status_code == 200
assert ...
Решение
# test_register_flow.py
import time
import requests
BASE_URL = 'https://apilearn.tukas.dev'
def test_register_and_login():
username = f'pytest_{int(time.time())}'
password = 'Secure123!'
reg = requests.post(f'{BASE_URL}/api/users/register/', json={
'username': username,
'email': f'{username}@example.com',
'password': password,
'first_name': 'Test',
'last_name': 'User',
})
assert reg.status_code in (200, 201)
auth = requests.post(f'{BASE_URL}/api/auth/token/', json={
'username': username,
'password': password,
})
assert auth.status_code == 200
token = auth.json()['token']
profile = requests.get(
f'{BASE_URL}/api/users/profile/',
headers={'Authorization': f'Token {token}'},
)
assert profile.status_code == 200
assert profile.json()['username'] == username
Тестирование ограничений пагинации
## conftest.py
import pytest
@pytest.fixture(scope='session')
def base_url():
return 'https://apilearn.tukas.dev'
# test_pagination.py
import requests
def test_products_returns_results(base_url):
resp = requests.get(f'{base_url}/api/products/')
...
def test_page_size_two(base_url):
resp = requests.get(f'{base_url}/api/products/?page_size=2')
...
def test_second_page(base_url):
resp = requests.get(f'{base_url}/api/products/?page=2&page_size=2')
...
Решение
# conftest.py
import pytest
@pytest.fixture(scope='session')
def base_url():
return 'https://apilearn.tukas.dev'
# test_pagination.py
import requests
def test_products_returns_results(base_url):
resp = requests.get(f'{base_url}/api/products/')
assert resp.status_code == 200
data = resp.json()
assert 'results' in data
assert isinstance(data['results'], list)
assert len(data['results']) > 0
def test_page_size_two(base_url):
resp = requests.get(f'{base_url}/api/products/?page_size=2')
assert resp.status_code == 200
results = resp.json()['results']
assert len(results) <= 2
def test_second_page(base_url):
resp = requests.get(f'{base_url}/api/products/?page=2&page_size=2')
assert resp.status_code == 200
Аутентификация уровня session для нескольких тестов
## conftest.py
import pytest
import requests
@pytest.fixture(scope='session')
def base_url():
return 'https://apilearn.tukas.dev'
@pytest.fixture(scope='session')
def auth_headers(base_url):
resp = requests.post(f'{base_url}/api/auth/token/', json={
'username': 'testuser',
'password': ...,
})
resp.raise_for_status()
token = ...
return {'Authorization': f'Token {token}'}
# test_authenticated.py
import requests
def test_profile_username(base_url, auth_headers):
...
def test_products_with_auth(base_url, auth_headers):
...
def test_echo_with_auth(base_url, auth_headers):
...
Решение
# conftest.py
import pytest
import requests
@pytest.fixture(scope='session')
def base_url():
return 'https://apilearn.tukas.dev'
@pytest.fixture(scope='session')
def auth_headers(base_url):
resp = requests.post(f'{base_url}/api/auth/token/', json={
'username': 'testuser',
'password': 'TestUser2024!',
})
resp.raise_for_status()
token = resp.json()['token']
return {'Authorization': f'Token {token}'}
# test_authenticated.py
import requests
def test_profile_username(base_url, auth_headers):
resp = requests.get(f'{base_url}/api/users/profile/', headers=auth_headers)
assert resp.status_code == 200
assert resp.json()['username'] == 'testuser'
def test_products_with_auth(base_url, auth_headers):
resp = requests.get(f'{base_url}/api/products/', headers=auth_headers)
assert resp.status_code == 200
def test_echo_with_auth(base_url, auth_headers):
resp = requests.get(f'{base_url}/api/echo/', headers=auth_headers)
assert resp.status_code == 200
Одна и та же фича: мокированный vs реальный HTTP
## test_count_comparison.py
import requests
from unittest.mock import patch
def get_product_count(base_url):
resp = requests.get(f'{base_url}/api/products/')
resp.raise_for_status()
return resp.json()['count']
def test_get_product_count_unit():
# Этот тест: быстрый, без сети.
# Не обнаруживает: переименованное поле в реальном API, неверный URL, проблемы с аутентификацией.
with patch('requests.get') as mock_get:
mock_get.return_value.status_code = 200
mock_get.return_value.raise_for_status.return_value = None
mock_get.return_value.json.return_value = ...
result = get_product_count('http://fake')
assert result == ...
def test_get_product_count_integration():
# Этот тест: медленнее, реальная сеть.
# Не обнаруживает: логические баги внутри get_product_count (покрывается юнит-тестом).
result = get_product_count('https://apilearn.tukas.dev')
assert ...
Решение
# test_count_comparison.py
import requests
from unittest.mock import patch
def get_product_count(base_url):
resp = requests.get(f'{base_url}/api/products/')
resp.raise_for_status()
return resp.json()['count']
def test_get_product_count_unit():
# Этот тест: быстрый, без сети.
# Не обнаруживает: переименованное поле в реальном API, неверный URL, проблемы с аутентификацией.
with patch('requests.get') as mock_get:
mock_get.return_value.status_code = 200
mock_get.return_value.raise_for_status.return_value = None
mock_get.return_value.json.return_value = {'count': 7, 'results': []}
result = get_product_count('http://fake')
assert result == 7
def test_get_product_count_integration():
# Этот тест: медленнее, реальная сеть.
# Не обнаруживает: логические баги внутри get_product_count (покрывается юнит-тестом).
result = get_product_count('https://apilearn.tukas.dev')
assert isinstance(result, int)
assert result >= 0
Фикстура уровня session, используемая тремя независимыми тестами
## conftest.py
import pytest
import requests
@pytest.fixture(scope='session')
def product_list():
resp = requests.get('https://apilearn.tukas.dev/api/products/')
assert resp.status_code == 200
return resp.json()['results']
# test_product_list.py
def test_products_not_empty(product_list):
assert len(product_list) > 0
def test_products_have_slug(product_list):
...
def test_products_have_name(product_list):
...
Решение
# conftest.py
import pytest
import requests
@pytest.fixture(scope='session')
def product_list():
resp = requests.get('https://apilearn.tukas.dev/api/products/')
assert resp.status_code == 200
return resp.json()['results']
# test_product_list.py
def test_products_not_empty(product_list):
assert len(product_list) > 0
def test_products_have_slug(product_list):
assert all(isinstance(p.get('slug'), str) and len(p['slug']) > 0
for p in product_list)
def test_products_have_name(product_list):
assert all(isinstance(p.get('name'), str) and len(p['name']) > 0
for p in product_list)