问题
I want to mock flask-login
's current_user
under the template rendering. This function return the current logged user.
Right now I'm mocking the AnnonymousUserMixin
from flask-login
which is returned by default if the user is not authenticated. But this leads to all kind of juggles. If I could simply mock current_user
I would be able to create a Mocked object for it to return.
Here a sample of what I'm using today:
import unnittest
from flask_login.mixins import AnonymousUserMixin
class TestFoo(unittest.TestCase):
@patch.object(AnonymousUserMixin, 'is_admin', create=True,
return_value=False)
@patch.object(AnonymousUserMixin, 'is_authenticated', return_value=True)
def test_user_restriction(self, *args):
...
Regards,
回答1:
Okay. I found the answer.
flask-login
will ask you to initialize a LoginManager
instance with login_manager.init_app(your_app)
. When you do this it add the current_user
to your app contexts processors. This happens at flask_login.utils._user_context_processor
function, which is defined as
def _user_context_processor():
return dict(current_user=_get_user())
Here _get_user
is defined at the same module. What I do to mock current_user
is mock _get_user
at flask_login.utils
.
Here is a working example of how it can be done. I am printing the response content so people can see the result differing. A real test would not instantiate Test class by hand and should use unittest.main
or something appropriated.
from flask import Flask, render_template_string as render_string
from flask_login import LoginManager, UserMixin
app = Flask(__name__)
loginmgr = LoginManager(app)
loginmgr.init_app(app)
class User(UserMixin):
pass
@loginmgr.user_loader
def load_user(user_id):
return User.get(user_id)
@app.route('/')
def index():
return render_string('Hello, {{ current_user | safe }}')
if __name__ == '__main__':
import unittest
from unittest import mock
class Test:
def test(self):
client = app.test_client()
response = client.get('/')
data = response.data.decode('utf-8')
print(data)
@mock.patch('flask_login.utils._get_user')
def test_current_user(self, current_user):
user = mock.MagicMock()
user.__repr__ = lambda self: 'Mr Mocked'
current_user.return_value = user
client = app.test_client()
response = client.get('/')
data = response.data.decode('utf-8')
print(data)
t = Test()
t.test()
t.test_current_user()
Here is the output of it:
Hello, <flask_login.mixins.AnonymousUserMixin object at 0x7f9d5ddaaf60>
Hello, Mr Mocked
Regards,
回答2:
I found this tutorial interesting in the section The Test.
It says this:
current_user needs to be accessed within the context of a request (it is a thread-local object, just like flask.request). When self.client.post completes the request and every thread-local object is torn down. We need to preserve the request context so we can test our integration with Flask-Login. Fortunately, Flask’s test_client is a context manager, which means that we can use it in a with a statement and it will keep the context around as long as we need it:
So in simple words, you can log in your user via post request and the current_user
object will be available and then you can test everything you want in the code.
Here is an example:
with self.client:
response = self.client.post(url_for('users.login'),
data={'email': 'joe@joes.com', 'password': '12345'})
self.assert_redirects(response, url_for('index'))
self.assertTrue(current_user.name == 'Joe')
self.assertFalse(current_user.is_anonymous())
来源:https://stackoverflow.com/questions/47294304/how-to-mock-current-user-in-flask-templates