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

Hanxue picture Hanxue · Oct 20, 2017 · Viewed 7.9k times · Source

This is the function I wish to test

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

I call the function from a pytest class

def test_get_all_projects(db_session):
    all_projects = ProjectController.get_all_projects()

with the db_session fixture

@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()

This result in the error

>           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

Manually Calling create_access_token

I still get the same result when I call create_access_token in the fixture above

db.session = session
session._test_access_token = create_access_token(identity='pytest')

yield session

How can I fake JWT tokens during testing with pytest?

Answer

vimalloc picture vimalloc · Oct 20, 2017

@jwt_required only works in the context of a Flask request. You can send in the access token using the flask test client with the headers name option:

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

Optionally, you could unwrap the decorated method by using the __wrapped__ property. In your case, it would look like:

method_response = get_all_projects.__wrapped__()

Note that any calls to the flask-jwt-extended helper functions in your endpoint (such as get_jwt_identity(), current_user, etc). would not work this way, as they require a flask request context. You could get around this by mocking the flask-jwt-extended functions used inside the function, but that may be harder to maintain as the application grows and changes.