簡體   English   中英

flask-jwt-extended:測試期間的假授權標頭(pytest)

[英]flask-jwt-extended: Fake Authorization Header during testing (pytest)

這是我想測試的功能

@jwt_required
    def get_all_projects(self):
        # implementation not included here

我從 pytest 類調用該函數

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 請求的上下文。 您可以使用帶有標頭名稱選項的燒瓶測試客戶端發送訪問令牌:

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 函數來解決這個問題,但是隨着應用程序的增長和變化,這可能更難維護。

在單元測試期間偽造 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):
    # ...

這就是我最終所做的並且為我工作。 在 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_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.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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM