Как установить current_user для pytest?

Я пишу unit test для представления, которое использует текущий зарегистрированный пользователь в запросе:

@app.route('/vendors/create', methods=['GET', 'POST'])
@login_required
def create_vendors():
    vendor_form = VendorForm(request.form)
    if vendor_form.validate_on_submit():
        vendor = db.session.query(Vendors).filter(Vendors.name == vendor_form.name.data,
                                                  Vendors.users.contains(g.user)).first()
        if vendor:
            flash('There is a vendor with this name already.')
            return redirect(url_for('create_vendors', vendor_form=vendor_form))
        vendor = Vendors(name=vendor_form.name.data, aws_access_key=vendor_form.aws_access_key.data,
                         merchant_key=vendor_form.merchant_key.data, secret_key=vendor_form.secret_key.data)
        vendor.users.append(current_user)
        db.session.add(vendor)
        db.session.commit()
        return redirect(url_for('vendors'))
    return render_template('edit_vendor.html', vendor_form=vendor_form)

и тест:

def test_create_vendor(self):
    response = TestViews.client.post('/vendors/create', follow_redirects=True,
                                     data=dict(name='Name', aws_access_key='aws_access_key',
                                               merchant_key='merchant_key',
                                               secret_key='secret_key'))
    assert response.status_code == 200
    vendors = db.session.query(Vendors).filter(Vendors.aws_access_key == 'aws_access_key').all()
    assert len(vendors) == 1
    assert vendors[0].name == 'Name'

и в моих настройках LOGIN_DISABLED = True.

Моя проблема в том, что при запуске теста current_user никогда не устанавливается.

Я попытался войти в систему с использованием тестового клиента и обернуть мои тесты в with TestLogin.client:, но current_user просто не установлен. Любые идеи о том, как его установить?

Ответ 1

Рассмотрим приведенное ниже пример приложения, созданного мной для этого вопроса

from flask import Flask, request,
from flask_login import LoginManager
from flask_login.utils import login_required, current_user, login_user

app = Flask(__name__)
app.secret_key = 'super secret key'
app.config['SESSION_TYPE'] = 'filesystem'

login_manager = LoginManager()
login_manager.init_app(app)

class MyUser():
    name = "tarun"

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return 10


@login_manager.user_loader
def load_user(user_id):
    return MyUser()

@app.route("/login", methods=["POST"])
def login():
    login_user(MyUser())
    return "Authenticated"

@app.route("/", methods=["GET", "POST"])
@login_required
def index():
    return "Authenticated " # + current_user


if __name__ == "__main__":
    app.run(debug=True)

Существуют разные способы тестирования зарегистрированных потоков.

Отключить вход и @login_required

В этом случае вы не используете current_user в вызове запроса и просто защитите поток для аутентифицированного пользователя. В этом случае вы можете использовать подход ниже

import flasktest as flaskr

class FlaskrTestCase(unittest.TestCase):

    def setUp(self):
        flaskr.app.testing = True
        flaskr.app.config['LOGIN_DISABLED'] = True
        flaskr.app.login_manager.init_app(flaskr.app)
        self.app = flaskr.app.test_client()

    def test_without_login(self):
        res = self.app.post("/")

        assert "Unauthorized" in res.data

В этом случае тест завершится неудачно, так как пользователь сможет получить доступ к аутентифицированному потоку. И current_user будет установлен как анонимный пользователь. И это так же хорошо, как отключить @login_required.

Тестирование с использованием фактического входа

Для этого вам нужно убедиться, что вы можете войти в свое приложение, используя POST, в конечную точку, где вы действительно заходите в систему

import unittest

import flasktest as flaskr

class FlaskrTestCase(unittest.TestCase):

    def setUp(self):
        flaskr.app.testing = True
        self.app = flaskr.app.test_client()

    def tearDown(self):
        pass

    def test_login(self):
        self.app.post("/login")

        res = self.app.get("/")

        assert "Authenticated" in res.data

    def test_without_login(self):
        res = self.app.post("/")

        assert "Unauthorized" in res.data

Это даже правильно установит current_user. В моем случае я не принимал никаких параметров данных формы, в вашем случае вы будете использовать что-то вроде ниже

self.app.post('/login', data=dict(
        username=username,
        password=password
    ), follow_redirects=True)

Использование файлов cookie сеанса

В этом случае вы установите сеанс user_id и _fresh

def test_login_session(self):
    with self.app.session_transaction() as sess:
        sess['user_id'] = '12'
        sess['_fresh'] = True  # https://flask-login.readthedocs.org/en/latest/#fresh-logins

    res = self.app.get("/")

    assert "Authenticated" in res.data

Это, в свою очередь, вызовет

@login_manager.user_loader
def load_user(user_id):
    return MyUser()

Чтобы получить фактического пользователя на основе user_id, предоставленного нами в сеансе 12

Ответ 2

Просто продумайте flask_login.current_user и установите фиксированное возвращаемое значение, например 'testuser'