努力测试 flask-dance / flask-security / flask-sqlalchemy / pytest

struggling to test flask-dance / flask-security / flask-sqlalchemy / pytest

我的应用程序需要登录 google 以便以后使用 google api。我有 flask-dance、flask-security、flask-sqlalchemy 可以在我的开发系统中进行登录和注销。

我一直在努力的是使用 pytest 测试登录。我正在尝试通过调用 flask_security.login_user 强制登录,但是 test_login 失败了,就好像没有人登录一样。我怀疑这是上下文设置的问题,但我尝试了很多不同的方法还没有找到灵丹妙药。

不幸的是,虽然我在一般软件开发方面有很多经验,特别是 python,但我没有解决这个问题所需的 pytest / flask-dance / flask-security 背景。

在settings.py

class Testing():
    # default database
    SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'

    TESTING = True
    WTF_CSRF_ENABLED = False

    # need to set SERVER_NAME to something, else get a RuntimeError about not able to create URL adapter
    SERVER_NAME = 'dev.localhost'

    # need a default secret key - in production replace by config file
    SECRET_KEY = "<test secret key>"

    # fake  credentials
    GOOGLE_OAUTH_CLIENT_ID = 'fake-client-id'
    GOOGLE_OAUTH_CLIENT_SECRET = 'fake-client-secret'

    # need to allow logins in flask-security. see https://github.com/mattupstate/flask-security/issues/259
    LOGIN_DISABLED = False

在conftest.py

import pytest

from racesupportcontracts import create_app
from racesupportcontracts.dbmodel import db
from racesupportcontracts.settings import Testing

@pytest.fixture
def app():
    app = create_app(Testing)
    yield app

@pytest.fixture
def dbapp(app):
    db.drop_all()
    db.create_all()
    yield app

在test_basic.py

def login_test_user(email):
    from racesupportcontracts.dbmodel import db, User
    from flask_security import login_user
    user = User.query.filter_by(email=email).one()
    login_user(user)
    db.session.commit()

def test_login(dbapp):
    app = dbapp
    from racesupportcontracts.dbmodel import db, init_db
    from racesupportcontracts import user_datastore
    from flask import url_for
    # init_db should create at least superadmin, admin roles
    init_db(defineowner=False)
    useremail = 'testuser@example.com'
    with app.test_client() as client:
        create_user(useremail, 'superadmin')
        login_test_user(useremail)
        resp = client.get('/', follow_redirects=True)
        assert resp.status_code == 200
        assert url_for('admin.logout') in resp.data

当您调用 login_user() 时,会修改 flask.session 对象。但是,在使用测试客户端时,you can only modify flask.session inside of a session transaction。如果您这样做应该会起作用:

with app.test_client() as client:
    with client.session_transaction() as sess:
        sess["user_id"] = 1  # if you want user 1 to be logged in for this test

    resp = client.get('/', follow_redirects=True)
    # make whatever assertions you want

如果您从 GitHub 安装最新版本的 Flask-Login,您还可以使用 FlaskLoginClient class 使其更具可读性:

# in conftest.py
from flask_login import FlaskLoginClient

@pytest.fixture
def app():
    app = create_app(Testing)
    app.test_client_class = FlaskLoginClient
    yield app


# in test_basic.py
def test_login(app):
    user = User.query.filter_by(email='testuser@example.com').one()
    with app.test_client(user=user) as client:
        resp = client.get('/', follow_redirects=True)
        # make whatever assertions you want

不幸的是,the author of Flask-Login refuses to publish an update of the package to PyPI,所以你不能使用 PyPI 上的 Flask-Login 版本,你 从 [=29 安装=]. (我不知道他为什么拒绝发布更新。)