Consider the following code (not a very good design, but that's the point):
class A(object):
def __init__(self,filepath):
self._access_file_system(filepath)
def get(self):
return self._result_dict
class B(object):
def __init__(self,filepath1,filepath2):
self._filepath1 = filepath1
self._filepath2 = filepath2
def foo(self):
a1 = A(self._filepath1).get()
a2 = A(self._filepath2).get()
return a1['result']==a2['result']
Now, if I want to test B.foo()
, I need to mock A
(as it accesses the file-system inside the constructor).
To write a test that will make sure B.foo()
returns False
in case a1.get()
and a2.get()
provide different values, I also need to mock B.get()
.
So, the test function should probably look like the following:
import mock
mock_get = mock.MagicMock(side_effect=[{'result': 0}, {'result': 1}])
@mock.patch('__main__.A')
def test_foo(MockA):
b = B('/file1','/file2')
res = b.foo()
assert res
MockA.assert_any_call('/file1')
MockA.assert_any_call('/file2')
#Doesn't work -
#the assignment doesn't propagate into the objects instantiated inside foo()
#A.get = mock_get
#The assigned method propagates into the class definition,
#so it works - BUT WHY?!
a = A(None)
a.get = mock_get
b = B('/file1', '/file2')
res = b.foo()
assert not res
Now, the strange point - as may be seen from the comments inside the code, if we assign mock_get
to the class, it won't propagate, but if we create an instance and assign to it, it propagates to other instances of the class.
I suppose this behavior is related to internal mechanisms of mock
, so it's important for me to understand it, to make proper usage of this library with all it's rich functionality.
So, does anyone has a clue?
On the first case I can not see anywhere that you are patching the get
method. You should assign the mock value to the get
method of A
before B
is called. For instance why does the following test fail?:
import mock
mock_get = mock.MagicMock(side_effect=[{'result': 0}, {'result': 1}])
@mock.patch('__main__.A')
def test_foo(MockA):
MockA.get = mock_get
b = B('/file1','/file2')
res = b.foo()
assert not res
MockA.assert_any_call('/file1')
MockA.assert_any_call('/file2')
The reason for the previous behaviour is that we are forgetting to patch the return value of the object (A), in this case MockA
, instead of the object itself ( MockA
). The A
object is the result of instantiating the A
class and you should access the method throgh the return_value
of the A
class. In your example it would be something similar to this:
import mock
mock_get = mock.MagicMock(side_effect=[{'result': 0}, {'result': 1}])
@mock.patch('__main__.A')
def test_foo(MockA):
MockA.return_value.get = mock_get
b = B('/file1','/file2')
res = b.foo()
assert res
MockA.assert_any_call('/file1')
MockA.assert_any_call('/file2')
You can check some of the following posts for more info on common python unit testing pitfalls:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.