Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specify return value of mocked function with pytest-mock?

The below prints False. Is this not how mocking works?

I tried changing the path to the function, but it errors out, so the path seems correct. What am I missing?

import pytest

from deals.services.services import is_user_valid


class TestApi:
    def test_api(self, mocker):
        mocker.patch('deals.services.services.is_user_valid', return_value=True)
        print(is_user_valid("sdfds", "sdfsdf"))
like image 318
M3RS Avatar asked Jun 18 '20 12:06

M3RS


2 Answers

The issue here is that you're essentially doing the following:

from deals.services.services import is_user_valid
import deals.services.services
deals.services.services.is_user_valid = Mock(return_value=True)
# call local is_user_valid

By importing the "terminal" symbol itself you've shorted any possibility of mocking, it's now a local reference, and so updating the "remote" reference will have no effect on the local version. Meaning you should keep a handle on the module itself, such that the relevant symbol gets resolved on each access:

from deals.services import services

def test_api(mocker):
    mocker.patch('deals.services.services.is_user_valid', return_value=True)
    print(services.is_user_valid("sdfds", "sdfsdf"))

should work better.

This is also an issue with any module using such imports, they requiring patching the point of use rather than the point of definition because by the time the mock runs chances are the user module already has their copy.

See the documentation for some more details.

like image 93
Masklinn Avatar answered Nov 15 '22 04:11

Masklinn


My actual problem was more complicated than the minimal version above. The mocked function was used in a different file.

What helped was to patch the function where it's imported/used, NOT where it's defined. So provide mocker.patch the path to the file where the function is imported.

like image 28
M3RS Avatar answered Nov 15 '22 03:11

M3RS