1

My application requires login to google for later use of google apis. I have flask-dance, flask-security, flask-sqlalchemy working to the point where I can do the log in and log out in my development system.

What I've been struggling with is testing of the login using pytest. I am trying force the login with the call to flask_security.login_user, but test_login fails as if nobody is logged in. I suspect this is a problem because of context setting, but I have tried a lot of different things and haven't found the magic elixir.

Unfortunately, while I have a lot of experience in software development in general and python in particular, I don't have the pytest / flask-dance / flask-security background needed to solve this.

in 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

in 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

in 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
Lou K
  • 1,128
  • 2
  • 12
  • 31

1 Answers1

0

When you call login_user(), that modifies the flask.session object. However, when using the test client, you can only modify flask.session inside of a session transaction. It should work if you do this:

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

If you install the latest version of Flask-Login from GitHub, you can also use the FlaskLoginClient class to make this more readable:

# 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

Unfortunately, the author of Flask-Login refuses to publish an update of the package to PyPI, so you can't use the version of Flask-Login that is on PyPI, you have to install from GitHub. (I have no idea why he refuses to publish an update.)

singingwolfboy
  • 5,336
  • 3
  • 27
  • 32
  • Will assume this answer is correct and mark it so. But I have abandoned use of flask-dance due to https://github.com/singingwolfboy/flask-dance/issues/182. BTW, do you mean the author of flask-login refuses to publish an update? – Lou K Jul 16 '19 at 15:25
  • Ah but I see that this answer doesn't really depend on flask-dance, does it? Unfortunately, I've also abandoned my attempts of unit testing my code. :p – Lou K Jul 16 '19 at 19:00
  • @LouK Yes, I meant flask-login! I've edited my answer to be more correct, thank you. – singingwolfboy Jul 25 '19 at 07:41