Reputation: 12766
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
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
?
Upvotes: 16
Views: 14669
Reputation: 714
For me the answer from @smarlowucf was almost sufficient, I just needed to give true as a return function:
@patch('flask_jwt_extended.view_decorators.verify_jwt_in_request', lambda *_: True)
def test_cameras(client):
#...
Upvotes: 0
Reputation: 931
Old topic, but here's some additional insight about how to test functions with @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"))
Upvotes: 3
Reputation: 321
One option for faking JWT tokens during unit testing is to patch jwt_required. More specifically patch the underlying function verify_jwt_in_request
. This mocks the decorator and removes the need to create authorization tokens for the test.
from unittest.mock import patch
@patch('flask_jwt_extended.view_decorators.verify_jwt_in_request')
def test_get_all_projects(mock_jwt_required):
# ...
Upvotes: 12
Reputation: 164
In my case, I was using the @jwt.user_claims_loader
wrapper for admin roles. I was also using cookies for the production side of things. In order to take advantage of the user_claims_loader, I created a test like so:
# 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
As you can see, I also reset my JWT_TOKEN_LOCATION
to json
so that it isn't looking for cookies. I created another fixture to create the access token so I could use it across tests
# 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
}
And I used it in my tests:
# 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
As an example of what my resource looked like:
# 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
Upvotes: 0
Reputation: 1457
Here's what i ended up doing and works for me. In 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='[email protected]').first(), expires_delta=False, fresh=True)
testapp.authorization = ('Bearer', access_token)
return testapp
And then in your TestConfig, set the following flags for flask-jwt-extended:
JWT_HEADER_TYPE = 'Bearer'
JWT_BLACKLIST_ENABLED = False
Upvotes: 2
Reputation: 4187
@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.
Upvotes: 21