I am trying to use pytest and mocker to test a python package that I am writing. This is the outline of my repo (assuming package is called hoopla
)
hoopla
|- library
|- __init__.py
|- general
|- exceptions
|- bourhaha
|- tests
|- __init__.py
|- test_brouhaha
Inside general.py
I have some functions that can be used via the package and also files inside the package. For example:
general.py
contains the function validate_email_exists()
brouhaha.py
contains a function called create_username()
that calls the function validate_email_exists()
.Inside test_brouhaha.py
I want to mock the validate_email_exists()
call when testing the function create_username()
as it calls out to an external system.
When I try to mock this call using pytest and pytst-mock I get an error saying No Module... (see below).
# general.py
def validate_email_exists(email):
return True
# bourhaha.py
from .general import validate_email_exists
def create_username(email):
if not validate_email_exists(email):
return False
# create usename
return True
# test_bourhaha.py
from library.bourhaha import *
def test_create_username(mocker):
mock_email_exists = mocker.patch("library.bourhaha.general.validate_email_exists") # causes error
---
mock_email_exists = mocker.patch("library.general.validate_email_exists") # causes error
mock_email_exists.return_value = False
assert create_username("test") # Fails because value of validate_email_exists return True
---
In my code I initially mocked with
mock_email_exists = mocker.patch("library.brouhaha.general.validate_email_exists")
mock_email_exists.return_value = False
And this throws the error
ModuleNotFoundError: No module named 'library.brouhaha.general'; 'library.brouhaha' is not a package
When I try
mock_email_exists = mocker.patch("library.general.validate_email_exists")
mock_email_exists.return_value = False
There is no error however the test fails because the function is returning True
The problem is in the path used by the patch()
function. The correct path is:
# CORRECT PATCHING
mock_email_exists = mocker.patch("library.bourhaha.validate_email_exists")
See where to patch in the documentation to know other details about a correct use of patch()
.
In your case the file general.py
defines the function validate_email_exists
, but the file bourhaha.py
execute the import
instruction:
from .general import validate_email_exists
so it creates a name validate_email_exists
in its namespace. You have to change the object pointed by this name. With your patch()
you have changed the object in the place where it is defined.
In your code you set the return_value
of mock_email_exists
to False
. At this point you have to also change the assert
instruction because the function create_username()
return False
when the function validate_email_exists()
return False
.
So the correct test function is:
from library.bourhaha import *
def test_create_username(mocker):
# WATCH to the path = 'library.bourhaha.validate_email_exists'
mock_email_exists = mocker.patch("library.bourhaha.validate_email_exists")
mock_email_exists.return_value = False
# I have change the assert test (assert ---> assert not)
assert not create_username("test")
Your modules general.py
and bourhaha.py
are perfect.