正如马特解释的那样,这只是一个上下文问题。感谢他的解释,我提供了两种在单元测试期间切换身份的不同方法。
首先,让我们稍微修改一下应用程序的创建:
def _on_principal_init(sender, identity):
"Sets the roles for the 'admin' and 'member' identities"
if identity.id:
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.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"
# Using `flask.ext.principal` `Permission.require`...
# ... instead of Matt's decorators
@app.route('/admin_alt')
@admin_permission.require()
def connect_admin_alt():
return "OK"
return app
第一种可能性是创建一个函数,在我们测试中的每个请求之前加载一个身份。setUpClass
最简单的方法是在应用程序创建后在测试套件中声明它,使用app.before_request
装饰器:
class WorkshopTestOne(unittest.TestCase):
#
@classmethod
def setUpClass(cls):
app = create_app()
cls.app = app
cls.client = app.test_client()
@app.before_request
def get_identity():
idname = flask.request.args.get('idname', '') or None
print "Notifying that we're using '%s'" % idname
identity_changed.send(current_app._get_current_object(),
identity=Identity(idname))
然后,测试变为:
def test_admin(self):
r = self.client.get('/admin')
self.assertEqual(r.status_code, 403)
#
r = self.client.get('/admin', query_string={'idname': "member"})
self.assertEqual(r.status_code, 403)
#
r = self.client.get('/admin', query_string={'idname': "admin"})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data, "OK")
#
def test_admin_alt(self):
try:
r = self.client.get('/admin_alt')
except flask.ext.principal.PermissionDenied:
pass
#
try:
r = self.client.get('/admin_alt', query_string={'idname': "member"})
except flask.ext.principal.PermissionDenied:
pass
#
try:
r = self.client.get('/admin_alt', query_string={'idname': "admin"})
except flask.ext.principal.PermissionDenied:
raise
self.assertEqual(r.data, "OK")
(顺便说一句,最后的测试表明 Matt 的装饰器更容易使用......)
第二种方法使用test_request_context
带有 a 的函数with ...
来创建临时上下文。无需定义由 装饰的函数@app.before_request
,只需将要测试的路由作为参数传递,在上下文中test_request_context
发送信号并使用该方法identity_changed
.full_dispatch_request
class WorkshopTestTwo(unittest.TestCase):
#
@classmethod
def setUpClass(cls):
app = create_app()
cls.app = app
cls.client = app.test_client()
cls.testing = app.test_request_context
def test_admin(self):
with self.testing("/admin") as c:
r = c.app.full_dispatch_request()
self.assertEqual(r.status_code, 403)
#
with self.testing("/admin") as c:
identity_changed.send(c.app, identity=Identity("member"))
r = c.app.full_dispatch_request()
self.assertEqual(r.status_code, 403)
#
with self.testing("/admin") as c:
identity_changed.send(c.app, identity=Identity("admin"))
r = c.app.full_dispatch_request()
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data, "OK")