I'm not okay. It's a problem.
When I used the mock I wrote in Previous article, I got hooked on about 2 points.
Consider the case of testing a module that uses an external library as follows. (In the following example, http library requests is used as an external library. Some points of the following description depend on requests. The contents are as follows.) To test this module alone, you can mock the classes in the external library.
python:Module to be tested(A.target.Let it be py)
from requests import Session
#Use the Session class somewhere
s = Session()
Here, if you write as follows, you cannot mock the external library well.
External library mock(error)
import A
from mock import patch
# A.Mocking the Session class used in the target(I'm going)
with patch('requests.Session'):
#test
The point is how to import the external library in the module under test.
If from requests import Session
is specified, the Session class will be placed under the module space where the import was executed.
If the module under test is ʻA.target.py, it will be ʻA.target.Session
.
The module to be tested uses the Session class (ʻA.target.Session`) imported under it, so you have to mock this.
External library mock(correct)
import A
from mock import patch
# A.Mocking the Session class imported in target
with patch('A.target.Session'):
#test
On the other hand, what if you say ʻimport requests`?
Module to be tested(When not using from)
import requests
#Use the Session class somewhere
s = requests.Session()
This time, without importing the Session class under my control, I specify requests.Session
and the package of the external library.
Therefore, the mock target is also requests.Session
instead of ʻA.target.Session`.
External library mock(When not using from)
import A
from mock import patch
#Mocking the Session class in the requests package
with patch('requests.Session'):
#test
With patch
, the specified target attribute (method, etc.) is also automatically mocked. In this case, a Mock class called MagicMock is used by default.
Example that the attribute of the specified target is mocked
>>> import requests
>>> from mock import patch
# requests.The post method of the Session class is mocked
>>> with patch('requests.Session'):
s = requests.Session()
print(s.post)
...
<MagicMock name='Session().post' id='4401343792'>
Consider the case where the response mock is set as the return value of the post method of the requests.Session class that is automatically mocked above. Since the response of the post method has a'text'property, it is assumed that the same property (mock) is set in the mock response to be created.
Use PropertyMock to mock a property. Even if you use MagicMock, you cannot specify the return value of the property (= return value of getter).
Use MagicMock for properties(Failure)
>>> import requests
>>> from mock import patch, MagicMock
>>> with patch('requests.Session'):
s = requests.Session()
response = MagicMock()
# 'text'Mock properties with MagicMock(try to)
text = MagicMock()
text.return_value = 'property'
response.text = text
s.post.return_value = response
print(s.post().text)
...
# text.return_The value specified for value is not returned
<MagicMock name='Session().post().text' id='4506288536'>
However, simply replacing MagicMock with PropertyMock does not change the result.
Use PropertyMock for properties(Failure)
>>> import requests
>>> from mock import patch, MagicMock, PropertyMock
>>> with patch('requests.Session'):
s = requests.Session()
response = MagicMock()
#Use PropertyMock instead of MagicMock
text = PropertyMock()
text.return_value = 'property'
response.text = text
s.post.return_value = response
print(s.post().text)
...
#Still not working...
<PropertyMock name='Session().post().text' id='4506377240'>
This is a problem that occurs when trying to set a PropertyMock for a mock instance (the response mock in the above example). In this case, you need to set PropertyMock to the type object, not the mock object itself.
Use PropertyMock for properties(success)
>>> import requests
>>> from mock import patch, MagicMock, PropertyMock
>>> with patch('requests.Session'):
s = requests.Session()
response = MagicMock()
text = PropertyMock()
text.return_value = 'property'
#If you set a PropertyMock to another mock, set it to that type object, not the mock object itself.
type(response).text = text
s.post.return_value = response
print(s.post().text)
...
property
Both were properly written in the documentation ...
http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch http://www.voidspace.org.uk/python/mock/mock.html?highlight=propertymock#mock.PropertyMock
Let's read the document properly.
Recommended Posts