I cannot understand how mock patch works and if does it able to solve my problem.
I have 3 files: communication with external interface (a.py), business logic (b.py) and tests (test.py). I want to patch external interface that is used by business logic while running tests.
a.py:
class SomeProductionClassINeedPatch(object):
name = 'Production Class (communication with some external service)'
def do_something(self):
print '<some feature with external service>'
b.py:
import mock
from src.tmp.mocks.a import SomeProductionClassINeedPatch
class WorkingClass(object):
def some_method_that_uses_external_class(self, *args):
external = self._external
external.do_something()
@property
def _external(self):
if not hasattr(self, '_ext_obj' or not self._ext_obj):
self._ext_obj = SomeProductionClassINeedPatch()
print isinstance(self._ext_obj, mock.MagicMock) # False
return self._ext_obj
b = WorkingClass()
b.some_method_that_uses_external_class()
test.py:
import mock
from src.tmp.mocks.b import WorkingClass # class I want to test
@mock.patch('src.tmp.mocks.a.SomeProductionClassINeedPatch')
def test_some_method_of_working_class(external_mock=None, *args):
o = WorkingClass()
o.some_method_that_uses_external_class() # external interface wasn't patched: <some feature with external service> - but I need mock here!
print '<test> - '+str(isinstance(o._external, mock.MagicMock)) # False
test_some_method_of_working_class()
I expect that calling o.some_method_that_uses_external_class() in test.py will not actually use external interface, but mock object. But seems still actual object is used.
Also when I check instance of external interface object either in test.py or in b.py - I cannot make them to pass isinstance(object, MagicMock) check, it always return false. Even if I try to apply the same patch in b.py (as class decorator). What am I doing wrong?
I use python 2.7 and mock library 1.0 by Michael Foord if that matters.
As stated in Where to patch:
patch works by (temporarily) changing the object that a name points to with another one. There can be many names pointing to any individual object, so for patching to work you must ensure that you patch the name used by the system under test.
In your example, the code using the object you want patched is in module b
. When you call patch, the class has already been imported in module b
, so patching a
will have no effect on b
. You need instead to path the object in b
:
@mock.patch('src.tmp.mocks.b.SomeProductionClassINeedPatch')
This will give you the expected result, the first call from b
is unpatched, while the second call from test
used the mock object:
# python test.py
False
<some feature with external service>
True
<test> - True
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With