flask-jwt-extended:测试期间的假授权 Header (pytest)
flask-jwt-extended: Fake Authorization Header during testing (pytest)
这是我要测试的功能
@jwt_required
def get_all_projects(self):
# implementation not included here
我从 pytest 调用函数 class
def test_get_all_projects(db_session):
all_projects = ProjectController.get_all_projects()
使用 db_session
夹具
@pytest.fixture(scope='function')
def db_session(db, request):
"""Creates a new database session for a test."""
engine = create_engine(
DefaultConfig.SQLALCHEMY_DATABASE_URI,
connect_args={"options": "-c timezone=utc"})
DbSession = sessionmaker(bind=engine)
session = DbSession()
connection = engine.connect()
transaction = connection.begin()
options = dict(bind=connection, binds={})
session = db.create_scoped_session(options=options)
db.session = session
yield session
transaction.rollback()
connection.close()
session.remove()
这导致错误
> raise NoAuthorizationError("Missing {} Header".format(header_name))
E flask_jwt_extended.exceptions.NoAuthorizationError: Missing Authorization Header
../../.virtualenvs/my-app/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py:132: NoAuthorizationError
手动调用create_access_token
我在上面的夹具中调用 create_access_token
时仍然得到相同的结果
db.session = session
session._test_access_token = create_access_token(identity='pytest')
yield session
如何在 pytest
测试期间伪造 JWT 令牌?
@jwt_required
仅适用于 Flask 请求的上下文。您可以使用带有 headers 名称选项的烧瓶测试客户端发送访问令牌:
def test_foo():
test_client = app.test_client()
access_token = create_access_token('testuser')
headers = {
'Authorization': 'Bearer {}'.format(access_token)
}
response = test_client.get('/foo', headers=headers)
# Rest of test code here
您可以选择使用 __wrapped__
属性 解包装饰方法。在您的情况下,它看起来像:
method_response = get_all_projects.__wrapped__()
请注意,对 flask-jwt-extended 帮助程序的任何调用都会在您的端点中运行(例如 get_jwt_identity()
、current_user
等)。不会以这种方式工作,因为它们需要烧瓶请求上下文。您可以通过模拟函数内部使用的 flask-jwt-extended 函数来解决这个问题,但是随着应用程序的增长和变化,这可能更难维护。
这是我最终为我所做的工作。在 conftest.py:
@pytest.yield_fixture(scope='function')
def app():
_app = create_app(TestConfig)
ctx = _app.test_request_context()
ctx.push()
yield _app
ctx.pop()
@pytest.fixture(scope='function')
def testapp(app):
"""A Webtest app."""
testapp = TestApp(app)
with testapp.app.test_request_context():
access_token = create_access_token(identity=User.query.filter_by(email='test@test.com').first(), expires_delta=False, fresh=True)
testapp.authorization = ('Bearer', access_token)
return testapp
然后在您的 TestConfig 中,为 flask-jwt-extended 设置以下标志:
JWT_HEADER_TYPE = 'Bearer'
JWT_BLACKLIST_ENABLED = False
就我而言,我使用的是 @jwt.user_claims_loader
管理员角色包装器。我还在生产方面使用 cookie。为了利用 user_claims_loader,我创建了一个这样的测试:
# conftest.py
from my.app import create_app
@pytest.fixture
def app():
app = create_app(testing=True)
app.config['JWT_COOKIE_CSRF_PROTECT'] = False
app.config['JWT_TOKEN_LOCATION'] = 'json'
jwt = JWTManager(app)
add_user_claims_loader(jwt)
return app
如您所见,我还将我的 JWT_TOKEN_LOCATION
重置为 json
,这样它就不会寻找 cookie。我创建了另一个装置来创建访问令牌,这样我就可以在测试中使用它
# conftest.py
@pytest.fixture
def admin_json_access_token(app, client):
access_token = create_access_token({'username': 'testadmin',
'role': 'admin'})
return {
'access_token': access_token
}
我在测试中使用了它:
# test_user.py
def test_get_users(app, client, db, admin_json_access_token):
rep = client.get('/api/v1/users', json=admin_json_access_token)
assert rep.status_code == 200
作为我的资源的示例:
# my/resources/admin/api.py
class Users(Resource):
@jwt_required
@admin_required # custom wrapper that checks the claims
def get(self):
all_users = User.query.all()
return all_users, 200
在单元测试期间伪造 JWT 令牌的一个选项是修补 jwt_required。更具体地说,修补底层函数 verify_jwt_in_request
。这模拟了装饰器并消除了为测试创建授权令牌的需要。
from unittest.mock import patch
@patch('flask_jwt_extended.view_decorators.verify_jwt_in_request')
def test_get_all_projects(mock_jwt_required):
# ...
老话题,但这里有一些关于如何使用@jwt_required测试函数的额外见解:
@pytest.fixture(scope="function", autouse=True)
def no_jwt(monkeypatch):
"""Monkeypatch the JWT verification functions for tests"""
monkeypatch.setattr("flask_jwt_extended.verify_jwt_in_request", lambda: print("Verify"))
这是我要测试的功能
@jwt_required
def get_all_projects(self):
# implementation not included here
我从 pytest 调用函数 class
def test_get_all_projects(db_session):
all_projects = ProjectController.get_all_projects()
使用 db_session
夹具
@pytest.fixture(scope='function')
def db_session(db, request):
"""Creates a new database session for a test."""
engine = create_engine(
DefaultConfig.SQLALCHEMY_DATABASE_URI,
connect_args={"options": "-c timezone=utc"})
DbSession = sessionmaker(bind=engine)
session = DbSession()
connection = engine.connect()
transaction = connection.begin()
options = dict(bind=connection, binds={})
session = db.create_scoped_session(options=options)
db.session = session
yield session
transaction.rollback()
connection.close()
session.remove()
这导致错误
> raise NoAuthorizationError("Missing {} Header".format(header_name))
E flask_jwt_extended.exceptions.NoAuthorizationError: Missing Authorization Header
../../.virtualenvs/my-app/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py:132: NoAuthorizationError
手动调用create_access_token
我在上面的夹具中调用 create_access_token
时仍然得到相同的结果
db.session = session
session._test_access_token = create_access_token(identity='pytest')
yield session
如何在 pytest
测试期间伪造 JWT 令牌?
@jwt_required
仅适用于 Flask 请求的上下文。您可以使用带有 headers 名称选项的烧瓶测试客户端发送访问令牌:
def test_foo():
test_client = app.test_client()
access_token = create_access_token('testuser')
headers = {
'Authorization': 'Bearer {}'.format(access_token)
}
response = test_client.get('/foo', headers=headers)
# Rest of test code here
您可以选择使用 __wrapped__
属性 解包装饰方法。在您的情况下,它看起来像:
method_response = get_all_projects.__wrapped__()
请注意,对 flask-jwt-extended 帮助程序的任何调用都会在您的端点中运行(例如 get_jwt_identity()
、current_user
等)。不会以这种方式工作,因为它们需要烧瓶请求上下文。您可以通过模拟函数内部使用的 flask-jwt-extended 函数来解决这个问题,但是随着应用程序的增长和变化,这可能更难维护。
这是我最终为我所做的工作。在 conftest.py:
@pytest.yield_fixture(scope='function')
def app():
_app = create_app(TestConfig)
ctx = _app.test_request_context()
ctx.push()
yield _app
ctx.pop()
@pytest.fixture(scope='function')
def testapp(app):
"""A Webtest app."""
testapp = TestApp(app)
with testapp.app.test_request_context():
access_token = create_access_token(identity=User.query.filter_by(email='test@test.com').first(), expires_delta=False, fresh=True)
testapp.authorization = ('Bearer', access_token)
return testapp
然后在您的 TestConfig 中,为 flask-jwt-extended 设置以下标志:
JWT_HEADER_TYPE = 'Bearer'
JWT_BLACKLIST_ENABLED = False
就我而言,我使用的是 @jwt.user_claims_loader
管理员角色包装器。我还在生产方面使用 cookie。为了利用 user_claims_loader,我创建了一个这样的测试:
# conftest.py
from my.app import create_app
@pytest.fixture
def app():
app = create_app(testing=True)
app.config['JWT_COOKIE_CSRF_PROTECT'] = False
app.config['JWT_TOKEN_LOCATION'] = 'json'
jwt = JWTManager(app)
add_user_claims_loader(jwt)
return app
如您所见,我还将我的 JWT_TOKEN_LOCATION
重置为 json
,这样它就不会寻找 cookie。我创建了另一个装置来创建访问令牌,这样我就可以在测试中使用它
# conftest.py
@pytest.fixture
def admin_json_access_token(app, client):
access_token = create_access_token({'username': 'testadmin',
'role': 'admin'})
return {
'access_token': access_token
}
我在测试中使用了它:
# test_user.py
def test_get_users(app, client, db, admin_json_access_token):
rep = client.get('/api/v1/users', json=admin_json_access_token)
assert rep.status_code == 200
作为我的资源的示例:
# my/resources/admin/api.py
class Users(Resource):
@jwt_required
@admin_required # custom wrapper that checks the claims
def get(self):
all_users = User.query.all()
return all_users, 200
在单元测试期间伪造 JWT 令牌的一个选项是修补 jwt_required。更具体地说,修补底层函数 verify_jwt_in_request
。这模拟了装饰器并消除了为测试创建授权令牌的需要。
from unittest.mock import patch
@patch('flask_jwt_extended.view_decorators.verify_jwt_in_request')
def test_get_all_projects(mock_jwt_required):
# ...
老话题,但这里有一些关于如何使用@jwt_required测试函数的额外见解:
@pytest.fixture(scope="function", autouse=True)
def no_jwt(monkeypatch):
"""Monkeypatch the JWT verification functions for tests"""
monkeypatch.setattr("flask_jwt_extended.verify_jwt_in_request", lambda: print("Verify"))