Skip to content

Commit

Permalink
Merge 28ea1b0 into b07c159
Browse files Browse the repository at this point in the history
  • Loading branch information
singingwolfboy committed May 31, 2019
2 parents b07c159 + 28ea1b0 commit 2e0de05
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGES
Expand Up @@ -3,6 +3,10 @@ Flask-Login Changelog

Here you can see the full list of changes between each Flask-Login release.

Unreleased
----------
- New custom test client: `flask_login.FlaskLoginClient`.
You can use this to write clearer automated tests. #431

Version 0.4.1
-------------
Expand Down
6 changes: 4 additions & 2 deletions docs/conf.py
Expand Up @@ -226,7 +226,9 @@


# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/3': None,
'http://flask.pocoo.org/docs/': None}
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"flask": ("http://flask.pocoo.org/docs/", None),
}

auto_content = 'both'
30 changes: 30 additions & 0 deletions docs/index.rst
Expand Up @@ -460,6 +460,34 @@ the session depending on a flag you set on the request. For example::
This prevents setting the Flask Session cookie whenever the user authenticated
using your `~LoginManager.header_loader`.

Automated Testing
=================
To make it easier for you to write automated tests, Flask-Login provides a
custom test client class that will set the user's login cookie for you.
To use this custom test client class, assign it to the
:attr:`test_client_class <flask.Flask.test_client_class>` attribute
on your application object, like this::

from flask_login import FlaskLoginClient

app.test_client_class = FlaskLoginClient

Next, use the :meth:`app.test_client() <flask.Flask.test_client>` method
to make a test client, as you normally do. However, now you can pass a
user object to this method, and your client will be automatically
logged in with this user!

.. code-block:: python
def test_simple(self):
user = User.query.get(1)
with app.test_client(user=user) as client:
# this request has user 1 already logged in!
resp = client.get("/")
Note that you must use a keyword argument, not a positional argument.
``test_client(user=user)`` will work, but ``test_client(user)``
will not.

Localization
============
Expand Down Expand Up @@ -564,6 +592,8 @@ Utilities
---------
.. autofunction:: login_url

.. autoclass:: FlaskLoginClient


Signals
-------
Expand Down
2 changes: 2 additions & 0 deletions flask_login/__init__.py
Expand Up @@ -19,6 +19,7 @@
user_loaded_from_header, user_loaded_from_request,
user_login_confirmed, user_unauthorized,
user_needs_refresh, user_accessed, session_protected)
from .test_client import FlaskLoginClient
from .utils import (current_user, login_url, login_fresh, login_user,
logout_user, confirm_login, login_required,
fresh_login_required, set_login_view, encode_cookie,
Expand All @@ -27,6 +28,7 @@

__all__ = [
LoginManager.__name__,
FlaskLoginClient.__name__,
UserMixin.__name__,
AnonymousUserMixin.__name__,
__version__,
Expand Down
19 changes: 19 additions & 0 deletions flask_login/test_client.py
@@ -0,0 +1,19 @@
from flask.testing import FlaskClient


class FlaskLoginClient(FlaskClient):
"""
A Flask test client that knows how to log in users
using the Flask-Login extension.
"""

def __init__(self, *args, **kwargs):
user = kwargs.pop("user", None)
fresh = kwargs.pop("fresh_login", True)

super(FlaskLoginClient, self).__init__(*args, **kwargs)

if user:
with self.session_transaction() as sess:
sess["user_id"] = user.id
sess["_fresh"] = fresh
63 changes: 62 additions & 1 deletion test_login.py
Expand Up @@ -34,7 +34,8 @@
user_needs_refresh, make_next_param, login_url,
login_fresh, login_required, session_protected,
fresh_login_required, confirm_login, encode_cookie,
decode_cookie, set_login_view, user_accessed)
decode_cookie, set_login_view, user_accessed,
FlaskLoginClient)
from flask_login.__about__ import (__title__, __description__, __url__,
__version_info__, __version__, __author__,
__author_email__, __maintainer__,
Expand Down Expand Up @@ -1726,3 +1727,63 @@ def login():
self.assertEqual(result.status_code, 302)
self.assertEqual(result.location,
'http://good.com/login?next=%2Fsecret')


class CustomTestClientTestCase(unittest.TestCase):
def setUp(self):
self.app = Flask(__name__)
self.app.config['SECRET_KEY'] = 'deterministic'
self.app.config['SESSION_PROTECTION'] = None
self.remember_cookie_name = 'remember'
self.app.config['REMEMBER_COOKIE_NAME'] = self.remember_cookie_name
self.login_manager = LoginManager()
self.login_manager.init_app(self.app)
self.app.config['LOGIN_DISABLED'] = False
self.app.test_client_class = FlaskLoginClient

@self.app.route('/')
def index():
return u'Welcome!'

@self.app.route('/username')
def username():
if current_user.is_authenticated:
return current_user.name
return u'Anonymous'

@self.app.route('/is-fresh')
def is_fresh():
return unicode(login_fresh())

@self.login_manager.user_loader
def load_user(user_id):
return USERS[int(user_id)]

# This will help us with the possibility of typoes in the tests. Now
# we shouldn't have to check each response to help us set up state
# (such as login pages) to make sure it worked: we will always
# get an exception raised (rather than return a 404 response)
@self.app.errorhandler(404)
def handle_404(e):
raise e

unittest.TestCase.setUp(self)

def test_no_args_to_test_client(self):
with self.app.test_client() as c:
result = c.get('/username')
self.assertEqual(u'Anonymous', result.data.decode('utf-8'))

def test_user_arg_to_test_client(self):
with self.app.test_client(user=notch) as c:
username = c.get('/username')
self.assertEqual(u'Notch', username.data.decode('utf-8'))
is_fresh = c.get('/is-fresh')
self.assertEqual(u'True', is_fresh.data.decode('utf-8'))

def test_fresh_login_arg_to_test_client(self):
with self.app.test_client(user=creeper, fresh_login=False) as c:
username = c.get('/username')
self.assertEqual(u'Creeper', username.data.decode('utf-8'))
is_fresh = c.get('/is-fresh')
self.assertEqual(u'False', is_fresh.data.decode('utf-8'))

0 comments on commit 2e0de05

Please sign in to comment.