Конфигурация PyTest для тестирования Flask API при использовании Flask-Login (Flask-Admin, Flask-Security) при которой тесты обходят авторизацию на сайте.
Описание проблемы
При тестировании, особенно при тестировании API для прохождения тестов необходимо обойти авторизацию на сайте. Для этого будем использовать встроенные функции. Ниже две конфигурации при которой всё работает.
Код учитывает обход как @login_required для путей API, так и авторизирует пользователя при прохождении тестов. В результате flask_login.current_user.id видит указанного пользователя.
Конфигурация 1
Содержимое файла conftest.py. Этот файл используется для инициализации тестов.
import pytest
from app import app, db
from app.models.users import User
@pytest.fixture
def client():
app.config["TESTING"] = True
# Обходит @login_required
app.config["LOGIN_DISABLED"] = True
with app.test_client() as client:
yield client
@pytest.fixture
def authenticated_request():
# Решает проблему с flask_login.current_user.id:
# AttributeError: 'AnonymousUser' object has no attribute 'id'
# Создает авторизацию пользователя
with app.test_request_context():
test_user = db.session.query(User).filter_by(id=1).first()
yield flask_login.login_user(test_user)
Содержимое файла test-category.py
import pytest
from app import db
from app.models.categories import Category
from app.repositories.category import get_category_by_id
headers = {"Content-Type": "application/json"}
@pytest.mark.usefixtures("authenticated_request")
def test_category_rename(client):
# Create test data
user_id = 1
cat = Category(user_id=user_id, name='Test Category')
db.session.add(cat)
db.session.commit()
# Mock
mock_name = 'Test Category New'
mock_request_data = {
'cat_id': cat.id,
'name': mock_name,
}
# Rename category
url = 'http://127.0.0.1:5000/categories/update/'
client.patch(url, headers=headers, data=json.dumps(mock_request_data))
# Check new name
category = get_category_by_id(user_id, cat.id)
assert category.name == mock_name
# Clear test data
db.session.delete(category)
db.session.commit()
Ссылка на репозиторий в GitHub.
Конфигурация 2
Если не нужен обход авторизации во всех тестах можно обходить авторизацию в каждом отдельном тесте.
Содержимое файла conftest.py:
from app import app, db
@pytest.fixture
def client():
app.config["TESTING"] = True
# Bypasses @login_required
app.config["LOGIN_DISABLED"] = True
with app.test_client() as client:
yield client
Содержимое файла test_category.py:
from app import db, app
from app.models.categories import Category
from app.models.users import User
from app.repositories.category import get_category_by_id
headers = {
"Content-Type": "application/json",
}
def test_category_rename(client):
# Create test data
user_id = 1
cat = Category(user_id=user_id, name='Test Category')
db.session.add(cat)
db.session.commit()
# Mock
mock_name = 'Test Category New'
mock_request_data = {
'cat_id': cat.id,
'name': mock_name,
}
with app.test_client() as client:
# Решает проблему с flask_login.current_user.id:
# AttributeError: 'AnonymousUser' object has no attribute 'id'
test_user = db.session.query(User).filter_by(id=user_id).first()
@app.login_manager.request_loader
def load_user_from_request(request):
return test_user
# Tests starts here
url = 'http://127.0.0.1:5000/categories/update/'
client.patch(url, headers=headers, data=json.dumps(mock_request_data))
# Check new name
category = get_category_by_id(user_id, 576)
assert category.name == mock_name
# Clear test data
db.session.delete(category)
db.session.commit()
Ссылка на репозиторий в GitHub.
Запуск тестов
Запуск тестов осуществляется командой:
Дополнительные ключи:
# -v - отображает полное название тестов
# --disable-pytest-warnings - убирает warnings summary