我正在开发Flask应用程序并使用Flask-security进行用户身份验证(后面又使用Flask-login)。
我有一条需要身份验证的路由/user
。我正在尝试编写一个单元测试来测试,对于经过身份验证的用户,这会返回相应的响应。
在我的unittest中,我正在创建一个用户并以该用户身份登录:
from unittest import TestCase
from app import app, db
from models import User
from flask_security.utils import login_user
class UserTest(TestCase):
def setUp(self):
self.app = app
self.client = self.app.test_client()
self._ctx = self.app.test_request_context()
self._ctx.push()
db.create_all()
def tearDown(self):
if self._ctx is not None:
self._ctx.pop()
db.session.remove()
db.drop_all()
def test_user_authentication():
# (the test case is within a test request context)
user = User(active=True)
db.session.add(user)
db.session.commit()
login_user(user)
# current_user here is the user
print(current_user)
# current_user within this request is an anonymous user
r = test_client.get('/user')
在测试中current_user
返回正确的用户。但是,请求的视图始终返回AnonymousUser
作为current_user
。
/user
路由定义为:
class CurrentUser(Resource):
def get(self):
return current_user # returns an AnonymousUser
我很确定我只是不完全了解测试Flask请求上下文的工作原理。我已经阅读了这个Flask Request Context documentation,但仍然不了解如何进行这种特定的单元测试。
答案 0 :(得分:18)
问题是不同的请求上下文。
在普通的Flask应用程序中,每个请求都会创建一个新的上下文,该上下文将在整个链中重复使用,直到创建最终响应并将其发送回浏览器。
当您创建并运行Flask测试并执行请求(例如self.client.post(...)
)时,在收到响应后将丢弃上下文。因此,current_user
始终为AnonymousUser
。
要解决这个问题,我们必须告诉Flask在整个测试中重复使用相同的上下文。您可以通过简单地使用以下代码包装代码来实现:
with self.client:
您可以在以下精彩文章中阅读有关此主题的更多信息: https://realpython.com/blog/python/python-web-applications-with-flask-part-iii/
示例强>
在:
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')
后:
def test_that_something_works():
with self.client:
response = self.client.post('login', { username: 'James', password: '007' })
# success
assertEquals(current_user.username, 'James')
答案 1 :(得分:13)
问题是test_client.get()
调用会导致推送新的请求上下文,因此您在测试用例的setUp()
方法中推送的那个不是{{1}处理程序看到。
我认为文档的Logging In and Out和Test Adding Messages部分中显示的方法是测试登录的最佳方法。我们的想法是通过应用程序发送登录请求,就像普通客户端一样。这将负责在测试客户端的用户会话中注册登录用户。
答案 2 :(得分:3)
我不太喜欢显示的其他解决方案,主要是因为您必须将密码保存在单元测试文件中(并且我正在使用Flask-LDAP-Login,因此添加虚拟用户等非常明显。 ),所以我乱砍了它:
在我设置测试应用的地方,我添加了:
@app.route('/auto_login')
def auto_login():
user = ( models.User
.query
.filter_by(username="Test User")
.first() )
login_user(user, remember=True)
return "ok"
但是,我正在对烧瓶app的测试实例进行相当多的更改,比如使用不同的DB,我在其中构建它,因此添加路径不会使代码明显变得更加混乱。 Obv这条路线在真实应用中不存在。
然后我这样做:
def login(self):
response = self.app.test_client.get("/auto_login")
在test_client
之后完成任何操作都应该登录。
答案 3 :(得分:0)
从文档中:https://flask-login.readthedocs.io/en/latest/
在单元测试时全局关闭身份验证会很方便。为此,如果应用程序配置变量LOGIN_DISABLED设置为True,则将忽略此装饰器。