Reputation: 1780
I'm currently trying to use mock to test a package. Mock seems to not like it when I structure my python modules in different directories using __init__.py
My file tree is as follows:
.\pkg
|_ module
|_ __init__.py
|_ module.py
|_ tests
|_ __init__.py
|_ test_basic.py
When I try to mock the methods inside filesize using the following unit test:
@mock.patch('module.os')
def test_filesize(self, mock_os):
class file_info:
st_size = 1000
mock_os.path.isfile.return_value = True
mock_os.stat.return_value = file_info
output = self.response.file_size("filename")
I get an error with a traceback of:
======================================================================
ERROR: test_filesize (__main__.cl_test_build_search_url)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python36\lib\site-packages\mock\mock.py", line 1297, in patched
arg = patching.__enter__()
File "C:\Python36\lib\site-packages\mock\mock.py", line 1369, in __enter__
original, local = self.get_original()
File "C:\Python36\lib\site-packages\mock\mock.py", line 1343, in get_original
"%s does not have the attribute %r" % (target, name)
AttributeError: <module 'module' from 'C:\\Users\\username\\work\\open_source\\pkg\\module\\__init__.py'> does not have the attribute 'os'
I have imported the os module in module.py
but I think it's getting confused because the __init__.py
is confusing the interpreter?
I've looked at the mock.patch arguments available and
@mock.patch('module.os', create = True)
allowed the code to run but os doesn't end up being mocked at all and os.path.isfile
and os.stat
doesn't get overridden during run time.
Upvotes: 1
Views: 1822
Reputation: 5529
I wanted to add a little more to @Gang's answer, since this bit me. This is going to be a little confusing because of the above example names but...
Example from above:
.\pkg
|_ module
|_ __init__.py
|_ module.py
|_ tests
|_ __init__.py
|_ test_basic.py
Lets say in the pkg.module.module
you have a class named module
(same as the python file name)
module.py
from time import time
class module:
pass
and in your pkg.module __init__.py
you do something like
from .module import module
__all__ = (
"module",
)
If you want to patch time
in the module.py
:
mock.patch("pkg.module.module.time", ...)
Mock will get confused/blocked by the __init__.py
and give you an error like
E AttributeError: <class 'pkg.module.module.module'> does not have the attribute 'time'
I had to change the name of the file or class. Or even just change the case, in the example above.
from time import time
class Module:
pass
init.py
from .module import Module
__all__ = (
"Module",
)
Upvotes: 1
Reputation: 2768
Try to change this:
@mock.patch('module.os', create = True)
to this:
# module(directory) module(module.py)
@mock.patch('module.module.os', create = True)
I am NOT good at explaining, here is a digest from mock.py
explains:
target
should be a string in the form'package.module.ClassName'
. Thetarget
is imported and the specified object replaced with thenew
object, so thetarget
must be importable from the environment you are callingpatch
from. The target is imported when the decorated function is executed, not at decoration time.
Upvotes: 1