Testing Flask login and authentication? Testing Flask login and authentication? flask flask

Testing Flask login and authentication?


The problem is different request contexts.

In your normal Flask application, each request creates a new context which will be reused through the whole chain until creating the final response and sending it back to the browser.

When you create and run Flask tests and execute a request (e.g. self.client.post(...)) the context is discarded after receiving the response. Therefore, the current_user is always an AnonymousUser.

To fix this, we have to tell Flask to reuse the same context for the whole test. You can do that by simply wrapping your code with:

with self.client:

You can read more about this topic in the following wonderful article:https://realpython.com/blog/python/python-web-applications-with-flask-part-iii/

Example

Before:

def test_that_something_works():    response = self.client.post('login', { username: 'James', password: '007' })    # this will fail, because current_user is an AnonymousUser    assertEquals(current_user.username, 'James')

After:

def test_that_something_works():    with self.client:        response = self.client.post('login', { username: 'James', password: '007' })        # success        assertEquals(current_user.username, 'James')


The problem is that the test_client.get() call causes a new request context to be pushed, so the one you pushed in your the setUp() method of your test case is not the one that the /user handler sees.

I think the approach shown in the Logging In and Out and Test Adding Messages sections of the documentation is the best approach for testing logins. The idea is to send the login request through the application, like a regular client would. This will take care of registering the logged in user in the user session of the test client.


I didn't much like the other solution shown, mainly because you have to keep your password in a unit test file (and I'm using Flask-LDAP-Login, so it's nonobvious to add a dummy user, etc.), so I hacked around it:

In the place where I set up my test app, I added:

@app.route('/auto_login')def auto_login():    user = ( models.User             .query             .filter_by(username="Test User")             .first() )    login_user(user, remember=True)    return "ok"

However, I am making quite a lot of changes to the test instance of the flask app, like using a different DB, where I construct it, so adding a route doesn't make the code noticeably messier. Obv this route doesn't exist in the real app.

Then I do:

def login(self):    response = self.app.test_client.get("/auto_login")

Anything done after that with test_client should be logged in.