У меня есть работающее приложение flask, и я добавляю тестирование с использованием библиотеки pytest .
99% функциональности приложения требуют, чтобы пользователи входили в систему, и я использую flask -security для управления этим.
Приложение работает хорошо, однако я изо всех сил пытаюсь добавить тестирование - в частности, чтобы войти в систему тестового пользователя, а затем тестировать представления, украшенные декоратором login_required, из базового пакета flask -login. Я прочитал много уроков и вопросов, связанных со стековым потоком, но не смог «исправить» это ...
Я понимаю, как работать в контексте приложения, но считаю, что conftest.py настроен правильно для этого.
Код ниже. Кто-нибудь может подсказать, что я могу делать не так? Заранее большое спасибо.
application.py
def create_app(config_class=DevConfig):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
## add extensions
from user.models import User, Role
from user.forms import ExtendedRegisterForm, ExtendedConfirmRegisterForm
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security.init_app(app, user_datastore, register_form=ExtendedRegisterForm,
confirm_register_form=ExtendedConfirmRegisterForm)
## register blueprints
return app
tests / conftest.py
import pytest
import os
from application import create_app, security
from application import db as _db
from config import TestConfig
from user.models import User, Role
from .utils import valid_user, create_roles_users
@pytest.fixture(scope='module')
def test_client():
app = create_app(config_class=TestConfig)
testing_client = app.test_client()
# application context
ctx = app.app_context()
ctx.push()
yield testing_client
ctx.pop()
@pytest.fixture(scope='module')
def init_db():
_db.create_all()
create_roles_users(security, _db)
yield _db
_db.drop_all()
tests / utils.py
valid_user = {
"first_name": "Test",
"last_name": "User",
"email": "test_user@test.com",
"password": "test_pwd"
}
def login(client, username=valid_user['email'], password=valid_user['password']):
return client.post('/user/login', data=dict(
email=username,
password=password
), follow_redirects=True)
def logout(client):
return client.get('/user/logout', follow_redirects=True)
def create_roles_users(security, db):
# create roles
roles = ['submitter', 'approver', 'admin', 'db-admin', 'leave-tracker']
for role in roles:
security.datastore.find_or_create_role(name=role)
# create and activate users
users = []
users.append(valid_user)
created_users = []
for user_args in users:
created_user = security.datastore.create_user(**user_args)
security.datastore.activate_user(created_user)
created_users.append(created_user)
# assign submitter role to users
for user in created_users:
for role in roles:
r = security.datastore.find_or_create_role(role)
security.datastore.add_role_to_user(user, r)
db.session.commit()
тестовое представление после входа в систему
from tests.utils import login, logout, valid_user
def test_homepage_not_loggedin(test_client):
"""
GIVEN no authenticated user
WHEN the '/' page is requested (GET)
THEN check the user is forwarded to log in
"""
rv = test_client.get('/')
assert rv.status_code == 302
rv = test_client.get('/', follow_redirects=True)
assert b'Please log in' in rv.data
def test_homepage_loggedin(test_client, init_db):
"""
GIVEN logged in user
WHEN the '/timesheets' page is requested (GET)
THEN check the user is not requested to log in
"""
with test_client:
rv = login(test_client)
assert rv.status_code == 200
rv = test_client.get('/timesheets', follow_redirects=True)
assert b'Please log in' not in rv.data
После запуска pytest я получаю 'pass' в первом тесте (not_loggedin) и 'fail' во втором тесте (loggedin) - возвращенный текст показывает что пользователь все еще перенаправлен на страницу / user / login.
В конфигурации я переключил LOGIN_DISABLED = False
на LOGIN_DISABLED = True
, и он обходит ошибку, однако это приводит к AttributeError: 'AnonymousUser' object has no attribute 'id'
, поскольку пользователь не авторизован и, следовательно, анонимен.