单元测试烧瓶主体应用程序 [英] Unit-testing a flask-principal application
问题描述
在代码粘贴, test_member
和 test_admin_b
都失败,抱怨 PermissionDenied
。显然,我没有正确地声明用户。至少,有关用户角色的信息不在正确的上下文中。
任何有关上下文处理复杂性的帮助或洞察都将深表赞赏。
Flask-Principal不会为您在请求之间存储信息。无论你喜欢做什么,都由你来决定。记住这一点,并考虑一下你的测试。您可以在 setUpClass
方法中调用 test_request_context
方法。这创建了一个新的请求上下文。您还在测试中使用 self.client.get(..)
进行测试客户端调用。这些调用创建了另一个不相互共享的请求上下文。因此,您对 identity_changed.send(..)
的调用不会在检查权限的请求的上下文中发生。我已经提前编辑你的代码,使测试通过,希望它能帮助你理解。请特别注意在 create_app
方法中添加的 before_request
过滤器。
导入hmac
导入单元测试
$ b $ from functools导入包装
从hashlib导入sha1
导入烧瓶
$ b $ from flask.ext.principal import委托人,权限,角色需求,身份,\
identity_changed,identity_loaded current_app
$ b def roles_required( *角色):
装饰者,指定用户必须具有所有指定的角色。
示例::
@ app.route('/ dashboard')
@roles_required('admin','editor')
def dashboard():
返回'Dashboard'
当前用户必须同时拥有admin角色和`editor`角色以
的顺序来查看页面
:param args:所需的角色
来源:https://github.com/ mattupstate / flask-security /
def wra pper(fn):
@wraps(fn)
def deco_view(* args,** kwargs):
perms = [角色中的角色权限(RoleNeed(角色)] $ b perm.can():
#return _get_unauthorized_view()
flask.abort(403)
返回fn(* args,** kwargs)
return decorated_view
返回包装
$ b def roles_accepted(* roles):
Decorator指定用户必须至少有一个
指定的角色。示例::
@ app.route('/ create_post')
@roles_accepted('editor','author')
def create_post():
return 'Create Post'
当前用户必须在
中具有`editor`或`author`角色才能查看页面。
:param args:可能的角色。
def包装(fn):
@wraps(fn)
def deco_view(* args,** kwargs):
perm =权限(* [角色角色角色])
如果perm.can():
返回fn(* args,** kwargs)
flask.abort(403)
返回decorated_view
返回包装
def _on_principal_init(发件人,身份):
如果identity.id =='admin':
identity.provides。添加(RoleNeed('admin'))
identity.provides.add(RoleNeed('member'))
$ b $ def create_app():
app = flask .flask(__ name__)
app.debug = True
app.config.update(SECRET_KEY ='secret',TESTING = True)
principal = Principal(app)
identity_loaded。 connect(_on_principal_init)
@ app.before_request
def determine_identity():
#这是获取用户认证信息的地方,可以完成
#例如,你可以将用户信息存储在
#session中,或者在HTTP标头,查询字符串等等中查找认证
#...
identity_changed .send(current_app._get_current_object(),identity = Identity('admin'))
@ app.route('/')
def index():
return OK
@ app.route('/ member')
@roles_accepted('admin','member')
def role_needed():
return OK
@ app.route('/ admin')
@roles_required('admin')
def connect_admin():
返回OK
@ app.route('/ admin_b')
@ admin_permission.require()
def connect_admin_alt():
返回OK
返回应用程序
admin_permission =权限(RoleNeed('admin'))
类WorkshopTest(unittest.TestCase):
@classmethod
de f setUpClass(cls):
app = create_app()
cls.app = app
cls.client = app.test_client()
$ b $ def test_basic(self) :
r = self.client.get('/')
self.assertEqual(r.data,OK)
def test_member(self):
r = self.client.get('/ member')
self.assertEqual(r.status_code,200)
self.assertEqual(r.data,OK)
def test_admin_b(self):
r = self.client.get('/ admin_b')
self.assertEqual(r.status_code,200)
self.assertEqual(r.data,OK )
if __name__ =='__main__':
unittest.main()
All, I'm writing a flask application that depends on flask-principal for managing user roles. I'd like to write some simple unit tests to check which views can be accessed by which user. An example of code is posted on pastebin to avoid cluttering this post. In short, I define a few routes, decorating some so that they can be accessed only by users with the proper role, then try to access them in a test.
In the code pasted, the test_member
and test_admin_b
both fail, complaining about a PermissionDenied
. Obviously, I'm failing to declare the user properly; at least, the info about the user roles is not in the right context.
Any help or insight about the complexities of context processing will be deeply appreciated.
Flask-Principal does not store information for you between requests. It's up to you to do this however you like. Keep that in mind and think about your tests for a moment. You call the test_request_context
method in the setUpClass
method. This creates a new request context. You are also making test client calls with self.client.get(..)
in your tests. These calls create additional request contexts that are not shared between each other. Thus, your calls to identity_changed.send(..)
do not happen with the context of the requests that are checking for permissions. I've gone ahead and edited your code to make the tests pass in hopes that it will help you understand. Pay special attention to the before_request
filter I added in the create_app
method.
import hmac
import unittest
from functools import wraps
from hashlib import sha1
import flask
from flask.ext.principal import Principal, Permission, RoleNeed, Identity, \
identity_changed, identity_loaded current_app
def roles_required(*roles):
"""Decorator which specifies that a user must have all the specified roles.
Example::
@app.route('/dashboard')
@roles_required('admin', 'editor')
def dashboard():
return 'Dashboard'
The current user must have both the `admin` role and `editor` role in order
to view the page.
:param args: The required roles.
Source: https://github.com/mattupstate/flask-security/
"""
def wrapper(fn):
@wraps(fn)
def decorated_view(*args, **kwargs):
perms = [Permission(RoleNeed(role)) for role in roles]
for perm in perms:
if not perm.can():
# return _get_unauthorized_view()
flask.abort(403)
return fn(*args, **kwargs)
return decorated_view
return wrapper
def roles_accepted(*roles):
"""Decorator which specifies that a user must have at least one of the
specified roles. Example::
@app.route('/create_post')
@roles_accepted('editor', 'author')
def create_post():
return 'Create Post'
The current user must have either the `editor` role or `author` role in
order to view the page.
:param args: The possible roles.
"""
def wrapper(fn):
@wraps(fn)
def decorated_view(*args, **kwargs):
perm = Permission(*[RoleNeed(role) for role in roles])
if perm.can():
return fn(*args, **kwargs)
flask.abort(403)
return decorated_view
return wrapper
def _on_principal_init(sender, identity):
if identity.id == 'admin':
identity.provides.add(RoleNeed('admin'))
identity.provides.add(RoleNeed('member'))
def create_app():
app = flask.Flask(__name__)
app.debug = True
app.config.update(SECRET_KEY='secret', TESTING=True)
principal = Principal(app)
identity_loaded.connect(_on_principal_init)
@app.before_request
def determine_identity():
# This is where you get your user authentication information. This can
# be done many ways. For instance, you can store user information in the
# session from previous login mechanism, or look for authentication
# details in HTTP headers, the querystring, etc...
identity_changed.send(current_app._get_current_object(), identity=Identity('admin'))
@app.route('/')
def index():
return "OK"
@app.route('/member')
@roles_accepted('admin', 'member')
def role_needed():
return "OK"
@app.route('/admin')
@roles_required('admin')
def connect_admin():
return "OK"
@app.route('/admin_b')
@admin_permission.require()
def connect_admin_alt():
return "OK"
return app
admin_permission = Permission(RoleNeed('admin'))
class WorkshopTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
app = create_app()
cls.app = app
cls.client = app.test_client()
def test_basic(self):
r = self.client.get('/')
self.assertEqual(r.data, "OK")
def test_member(self):
r = self.client.get('/member')
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data, "OK")
def test_admin_b(self):
r = self.client.get('/admin_b')
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data, "OK")
if __name__ == '__main__':
unittest.main()
这篇关于单元测试烧瓶主体应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!