Giải thích, đó chỉ là vấn đề ngữ cảnh. Nhờ những lời giải thích của anh ấy, tôi đã có hai cách khác nhau để chuyển đổi danh tính trong các bài kiểm tra đơn vị.
Trước hết, chúng ta hãy thay đổi một chút sự sáng tạo ứng dụng:
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
Một khả năng đầu tiên là tạo một hàm tải một bản sắc trước mỗi yêu cầu trong thử nghiệm của chúng tôi. Các đơn giản nhất là phải khai báo nó trong setUpClass
của bộ ứng dụng thử nghiệm sau khi ứng dụng được tạo ra, bằng cách sử dụng app.before_request
trang trí:
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))
Sau đó, các bài kiểm tra trở thành:
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")
(Ngẫu nhiên, cuối cùng thử nghiệm cho thấy rằng trang trí của Matt là xa dễ dàng hơn để sử dụng ....)
một cách tiếp cận thứ hai sử dụng test_request_context
chức năng với một with ...
để tạo ngữ cảnh tạm thời.Không cần phải xác định một chức năng được trang trí bởi @app.before_request
, chỉ cần vượt qua các tuyến đường để kiểm tra như là đối số của test_request_context
, gửi identity_changed
tín hiệu trong bối cảnh và sử dụng phương pháp .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")
Đó là điều tôi sợ: Tôi bị mất trong ngữ cảnh. AFAIU, 'determ_identity' của bạn sẽ được gọi trước khi yêu cầu được xử lý, sử dụng cùng một ngữ cảnh, phải không? Vì vậy, tôi cần khai báo một danh tính ở đâu đó trong bối cảnh đó, hoặc lấy nó từ một bối cảnh toàn cục, hoặc tạo nó ngay lập tức từ một số đối số khác được truyền cho yêu cầu (ví dụ, 'query_string') ... Tôi sẽ thử để đăng một số giải pháp trong câu trả lời khác, tôi sẽ rất biết ơn nếu bạn có thể cho tôi biết suy nghĩ của bạn. –
Chính xác. Nhưng tôi không chắc tại sao bạn lại sợ điều này. Và có, hàm 'probability_identity' sẽ được gọi trên mọi yêu cầu và chia sẻ cùng một bối cảnh với các phương thức xem của bạn. Việc xác định danh tính tất cả phụ thuộc vào cách bạn định xác thực người dùng. Ví dụ, nếu bạn muốn một cơ chế xác thực dựa trên phiên, bạn nên ghép Flask-Principal với Flask-Login. Nếu bạn đang xây dựng một API không có trạng thái, bạn nên chuyển các tham số xác thực trong các tiêu đề hoặc sử dụng http auth cơ bản và xác định người dùng trong 'probability_identity' từ các giá trị đó. –
Một điều tôi không đề cập đến là Flask-Principal, theo mặc định, lưu danh tính trong phiên, vì vậy lần đầu tiên bạn gọi phương thức 'identity_changed.send' nó sẽ lưu trữ danh tính trong phiên và tải nó cho mọi yêu cầu ngoại trừ các điểm cuối tĩnh. –