4

I'm creating a Flask app with JWT Authorization and trying to test services with PyTest.

I successfully added tests to endpoints, but when I trying to add unit tests for certain function I can't access current user, because flask_jwt_extended.get_current_user() returns None.

Here is simple example:

@api.route('/listings', methods=['POST'])
@jwt_required
def create_listing():
    payload = request.json
    listing = listing_svc.create(payload)
    return listing


def create(payload):
    listing = ListingSchema().load(payload, db.session).data


class ListingSchema(ModelSchema):
    id = field_for(Project, 'id', dump_only=True)
    creator_user_id = field_for(Project, 'creator_user_id')
    # ...

    @pre_load
    def set_creator_id(self, data):
        current_user = flask_jwt_extended.get_current_user()
        data['creator_user_id'] = current_user.id

It works when I authorize and send a request using app_context:

with client.application.app_context():
    rv = client.post('/listings',
        # ...
    )

But what I need is to test create function without sending a request to client. In this case flask_jwt_extended.get_current_user() returns None, so I think I should set request context some way before running this function.

I tried to do this...

fake_payload = {}
with client.application.test_request_context('/listings', headers={'Authorization': 'Bearer ' + access_token}):
    create(fake_payload)

but still getting current_user is None

This is how I get token:

def login(email, password):
    user = get_by_email(email)
    if user and check_password_hash(user.password, password):
        return access_token = flask_jwt_extended.create_access_token(identity=email)
ShaVas
  • 41
  • 5
  • How are u generating the token? Please add it's code to the question – DarkSuniuM Apr 19 '19 at 13:12
  • Take a look at this for some examples of how testing could work: https://github.com/vimalloc/flask-jwt-extended/blob/master/tests/test_view_decorators.py#L47 – vimalloc Apr 19 '19 at 14:30
  • Or if you don’t want to test a full request you have a couple options. You could mock out the flask-jwt-extended functions to return a fake current user. Or even better, you could split that function up into to functions, one of them grabbing the current user from the pre request, which then calls another function that passes in the username, and unit test that second function in isolation. – vimalloc Apr 19 '19 at 14:42

3 Answers3

2

If you are writing unit tests, using mock can be helpful. For jwt authorization with flask-jwt-extended you can patch the verify_jwt_in_request method which is called from the jwt_required decorator. Then you can also patch the get_jwt_identity function to return a test username. For example:

from unittest.mock import patch


@patch('path.to.some.code.get_jwt_identity')
@patch('flask_jwt_extended.view_decorators.verify_jwt_in_request')
def test_some_code(mock_jwt_required, mock_jwt_identity):
    mock_jwt_identity.return_value = 'user1'

    # Test jwt protected endpoint

Note: This patch is specific to latest package version flask-jwt-extended==3.21.0. The code may change with new versions.

smarlowucf
  • 311
  • 3
  • 11
0

Question from a long time ago but here is the solution for further readers.

You need to activate the app_context, then the request_context and finally call the function decorator is calling, which is verify_jwt_in_request:


fake_payload = {}
with client.application.app_context():
    with client.application.test_request_context('/listings', headers={'Authorization': 'Bearer ' + access_token}):
        verify_jwt_in_request()
        create(fake_payload)

and now you have your current_user set

ttecles
  • 1
  • 2
-1

If you truly want to unit test you need to unit test one function at a time. This is true test driven development in my opinion. So first write tests for create then load and so on. Use patching to mock the functionality of calls to other functions.

l33tHax0r
  • 1,384
  • 1
  • 15
  • 31
  • Unit testing is great, but so is integration testing, especially when it comes to web apps. That's what this question is about. – Devin Sep 23 '20 at 16:07
  • O sorry... I read the question as "I successfully added tests to endpoints, but when I trying to add unit tests for certain function I can't access current user, because flask_jwt_extended.get_current_user() returns None." and then @smarlowucf gave a great answer on what I suggested. Thanks for the down vote :) – l33tHax0r Sep 24 '20 at 18:31