Flask-Login使用pytest

3

我想制作一个测试套件,登录到我的Flask应用程序,但它总是返回匿名用户。这是我的代码:

conftest.py:

import pytest
from wahoo_connect import init_app, db
from wahoo_connect.models import User

from dotenv import load_dotenv
load_dotenv('.flaskenv')

@pytest.fixture(scope='module')
def app():
    app = init_app()
    with app.app_context():
        db.create_all()
        user = User(username='testuser', email='test@gmail.com', forename='Test', surname='User', confirmed=True)
        user.set_password('testing')
        db.session.add(user)
        db.session.commit()
        yield app

@pytest.fixture(scope='module')
def client(app):
    return app.test_client()

测试:

def test_index_page__logged_in(client):
    with client:
        client.post('/auth/login', data=dict(username='testuser', password='testing'), follow_redirects=True)
        assert current_user.username == 'testuser'

我的登录路由:

@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    # Login route logic goes here
    if current_user.is_authenticated:
        return redirect(url_for('home_bp.index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password', 'warning')
            return redirect(url_for('auth_bp.login'))
        login_user(user, remember=form.remember_me.data)
        next_page = request.args.get('next')
        if not next_page or url_parse(next_page).netloc != '':
            next_page = url_for('home_bp.index')
        return redirect(next_page)
    return render_template('auth/login.html', title='Sign In', form=form)

测试套件的编写比代码更难!

你的应用程序中是否设置了一个“用户加载器”回调函数?如果没有,请确保根据flask-login文档进行正确配置:https://flask-login.readthedocs.io/en/latest/#how-it-works - Magnun Leno
我会调查一下。虽然在我的开发服务器上身份验证工作正常。 - MartynW
2个回答

2

我怀疑你的问题根源在于需要推送请求上下文:将with client:替换为with client.application.test_request_context():或者简单地使用app.test_request_context():

但是,每次需要身份验证时都向登录路由写入POST请求是很麻烦的,因为大多数路由都需要已登录的用户。为了让我们的生活更轻松,我们可以实现一个Flask-Login版本的建议方法,该方法在官方Flask测试教程中提到。请注意,我正在使用Flask-SQLAlchemy,但这也应该适用于没有它的情况。

在我的设置中,app装置不处理数据库设置和拆卸;相反,client装置实现db.create_alldb.drop_all以确保每个测试都有新鲜的db实例。在这种设置中,每次调用client装置时都包含一个测试用户是不可取的。

基本方法如教程所述:首先编写一个实现loginlogout方法的AuthActions类。我们使用一个auth fixture来请求client fixture并返回AuthActions的实例。测试函数可以请求auth fixture并在测试期间根据需要调用loginlogout方法。

这是AuthActions类和auth fixture,它依赖于其他地方定义的Users模型。

#conftest.py

import pytest
from app import init_app, db
from app.models import Users

class AuthActions():
    def __init__(self, client, username='TestUser', password='TestPass'):
        self.client = client
        self.username = username
        self.password = password

    def create(self):
        with self.client.application.app_context():
            test_user = Users(username=self.username, password=self.password)
            test_user.save()

    def login(self):
        return self.client.post(
            '/login',
            data={'username': self.username, 'password': self.password}
        )

    def logout(self):
        return self.client.get('/logout')

# Define client and other fixtures here ...

@pytest.fixture
def auth(client):
    return AuthActions(client)

现在让我们测试一个需要身份验证的路由。请记住,您需要推送请求上下文才能使身份验证正常工作:
#test_routes.py

def test_secret_route_unauthenticated(client):
    # passes
    with client.application.test_request_context():
        response = client.get('/secret')
        assert response.status_code == 403
        assert not current_user.is_authenticated

def test_secret_route_authenticated(client, auth):
    # passes
    with client.application.test_request_context():
        auth.create_user()
        auth.login()
        response = client.get('/secret')
        assert response.status_code == 200
        assert current_user.is_authenticated

def test_secret_route_authenticated(client, auth):
    # fails; no request context
    auth.create_user()
    auth.login()
    response = client.get('/secret')
    assert response.status_code == 200
    assert current_user.is_authenticated


0
你可以编写一个登录函数。
def login(client):
    """Login helper function"""
    return client.post(
        "auth/login",
        data=dict(username='testuser', password='testing'), 
        follow_redirects=True
    )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接