Matt説明したとおり、それは文脈の問題です。彼の説明のおかげで、私は単位テストの間にアイデンティティを切り替えるための2つの異なる方法がありました。すべての前に
、のビットにアプリケーションの作成を修正してみましょう:
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
第1の可能性は、我々のテストで各要求の前に身元をロードする関数を作成することです。最も簡単なアプリがapp.before_request
デコレータ使用して、作成された後にテストスイートのsetUpClass
でそれを宣言することです。そして、
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のデコレータを使用する方がはるかに簡単であることを示している第2のアプローチはでtest_request_context
機能を使用しています一時的なコンテキストを作成するにはをクリックします。@app.before_request
で装飾された関数を定義する必要はありません、ただ、test_request_context
の引数としてテストコンテキストでidentity_changed
信号を送信し、Mattの応答に伴い.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")
:私は文脈で迷子です。 AFAIU、あなたの 'decide_identity'はリクエストが処理される前に同じコンテキストを使って呼び出されます、そうですか?だから、私はその文脈のどこかでIDを宣言したり、グローバルな文脈からそれを取得したり、リクエストに渡された余分な引数(例えば、 'query_string')から即座にそれを作成する必要があります。別の答えにいくつかの解決策を投稿するには、あなたが思ったことを私に知らせることができれば非常に感謝します。 –
修正。しかし、なぜあなたがこれを恐れるのか分かりません。もちろん、リクエストごとに 'decide_identity'関数が呼び出され、ビューメソッドと同じコンテキストが共有されます。 IDの決定はすべて、ユーザーを認証する方法によって異なります。たとえば、セッションベースの認証メカニズムが必要な場合は、Flask-PrincipalとFlask-Loginを組み合わせる必要があります。ステートレスなAPIを構築する場合は、ヘッダーにauthパラメーターを渡すか、基本的なhttp authを使用して、それらの値から 'decide_identity'でユーザーを判断する必要があります。 –
私が言及しなかったことの1つは、デフォルトではFlask-PrincipalがセッションにIDを保存することです。つまり、初めてID_changed.sendメソッドを呼び出すと、IDはセッションに格納され、静的エンドポイントを除きます。 –