0

I am trying to build out some tests for a Flask application and I'm struggling to get some of the mocking/patching working as expected.

Despite spending time reading/thinking through the documentation around where to patch, neither @patch or monkeypatch appear to behave as I expect and I'm struggling to see what I'm doing wrong.

In the Flask api.py file I have a simple login API that checks an auth requirement (defined elsewhere and imported as shown):

from project_name.api.auth import login_auth

@api.route('/v1/login', methods=['POST'])

    data = request.get_json()

    username = data['Username']
    password = data['Password']

    if not login_auth(username, password):
        raise APIError('Invalid Username or Password')
    ...

and I'm interested in patching the login_auth function so that I can test without a real username/password.

In my pytest file, I have attempted to use both patch and monkeypatch to solve this.

@patch approach:

@patch('project_name.api.auth.login_auth')
def test_login(mock_login_auth, client):
    mock_login_auth.return_value = True
    mimetype = 'application/json'
    headers = {
        'Content-Type': mimetype,
        'Accept': mimetype
    }
    data = {
        'Username': "test_user",
        'Password': "test_password",
    }
    url = 'api/v1/login'

    assert project_name.api.auth.login_auth('x', 'y') == True                                                                                                                                                                                                               

    response = client.post(url, data=json.dumps(data), headers=headers)
    assert response.status_code == 200

And if I run this test then the first assert statement which calls the patched function directly will work as expected, however the API call will not use the mocked login_auth function and the test will fail this criteria.

monkeypatch approach:

def test_login2(client, monkeypatch):
    monkeypatch.setattr("project_name.api.auth.login_auth", lambda x, y: True)
    mimetype = 'application/json'
    headers = {
        'Content-Type': mimetype,
        'Accept': mimetype
    }
    data = {
        'Username': "test_user",
        'Password': "test_password",
    }
    url = 'api/v1/login'

    assert project_name.api.auth.login_auth('x', 'y') == True                                                                                                                                                                                                               

    response = client.post(url, data=json.dumps(data), headers=headers)
    assert response.status_code == 200

And again, I see the first assert, that calls the function directly, being patched as expected but the function that is being called after the request to the API has not been patched so the test will fail on the second assert statement.

I've tried a number of different approaches, including trying to patch the login_auth at the path project_name.api.login_auth (under the assumption that I'm trying to patch the specific login_auth function that gets imported into the api.py file) but the right approach is not clear to me.

I'm wondering if there is something specific I need to do given that I'm using Flask's test_client approach, or whether there is something else I'm missing.

anthr
  • 1,026
  • 4
  • 17
  • 34
  • Your decorator in the first example is invalid. – Maximilian Burszley Dec 12 '18 at 20:45
  • Take a look at [this question](https://stackoverflow.com/questions/53771263/mock-a-python-function-that-is-used-by-another-function). You your problem is similar. – Mauro Baraldi Dec 16 '18 at 12:57
  • This is precisely the same issue against which I've been bashing my head. I'd be wildly interested in finding out a working technique for this. – Adam Dec 21 '19 at 23:20

1 Answers1

0

The problem here is that in api.py you are importing a function of the module

from project_name.api.auth import login_auth

and it seems like the namespaces need to match, so as workaround you can import the whole module and call the funcion with the complete namespace:

if not project_name.api.auth.login_auth(username, password):

You can take a look to this other question too: mocking functions using python mock

Luis Sobrecueva
  • 680
  • 1
  • 6
  • 13