我试图用python unittest.mock测试我的代码。
实际上,我的测试方式很受这篇博文的启发: http://engineroom.trackmaven.com/blog/real-life-mocking/
以下是我想在其中模拟内容的模块的一部分:
class RestRequestManager:
"""
A Context Manager for class RestRequest.
This context manager is call using the "with" statement and
take a RestRequest object as parameter.
The point is to automate login and logout.
"""
def __init__(self, restReq):
self.restReq = restReq
def __enter__(self):
""" Enter : login() """
self.restReq.login()
return self.restReq
def __exit__(self, type, value, traceback):
""" Exit : logout() """
self.restReq.logout()
class RestRequest(RestRequestBase):
""" class RestRequest. """
def __init__(self, username, password, url_rest_base, endpoint, method, *args,
payload=None, outfile=None, file_mod=None):
super().__init__(username, password, url_rest_base, endpoint, method, *args)
self.method = method
self.payload = payload
self.outfile = outfile
self.file_mod = file_mod
self.response = None
def get_response(self):
return self.response
以下是使用此模块的Flask应用程序的一部分:
class CtrlmJobIdLogs(Resource):
""" Control-M log on jobid """
def get(self, jobid):
"""Get job's log."""
rest_req = restRequest.RestRequest(USERNAME, PASSWORD, URL_REST_BASE, ENDPOINT_LOGS,
'GET', jobid, payload=None, outfile=None,
file_mod=None)
with restRequest.RestRequestManager(rest_req) as req_man:
req_man.perform()
logging.debug('DEBUG>> : ctrlm_job --> {}'.format(jobid))
return {jobid: req_man.get_response().json()}
这是我的测试:
class MockTests(unittest.TestCase):
""" Test the rest module with mocks """
############################
#### setup and teardown ####
############################
def setUp(self):
""" Executed prior to each test """
app.config['TESTING'] = True
app.config['WTF_CSRF_ENABLED'] = False
app.config['DEBUG'] = False
self.client = app.test_client()
def tearDown(self):
""" executed after each test """
pass
###############
#### tests ####
###############
#@mock.patch('MonAPPflaskRest.flaskRest.restRequest.RestRequestManager')
@mock.patch('MonAPPflaskRest.flaskRest.restRequest.RestRequest')
def test_ctrlM_no_output(self, mock_RestRequest):
""" UAT 4 : ctrl-M job output is not found. """
print("\n\n======================================")
print("test_ctrlM_no_output")
print("======================================")
jobid_no_output = 'blabla:1epyx'
message_noutput = 'Job CTRL-M {} not found'.format(jobid_no_output)
expected_dict = { "blabla:1epyx": {"errors": [{ "message": "Request rejected by Data Center CTM5319 OUTPUT DOES NOT EXIST FOR THIS JOB"}]} }
# mock object :
mock_response = mock.MagicMock()
mock_response.json.return_value = expected_dict
mock_response.status_code.return_value = 500
# # the patched object returns values
mock_RestRequest.response.return_value = mock_response
#mock_RestRequest.return_value.response.return_value.json.return_value = expected_dict
# REST url to test
url = '/ctrlm/{}/output/'.format(jobid_no_output, follow_redirects=True)
# call
response_dict = self.client.get(url)
# https://stackoverflow.com/questions/28882226/convert-test-client-data-to-json
# assertions tests
mock_RestRequest.assert_called()
if __name__ == "__main__":
unittest.main()
Pytest ouptut是:
collected 1 item
tests\test_mock.py F
================================== FAILURES ===================================
_______________________ MockTests.test_ctrlM_no_output ________________________
self = <test_mock.MockTests testMethod=test_ctrlM_no_output>
mock_RestRequest = <MagicMock name='RestRequest' id='79830480'>
@mock.patch('MonAPPflaskRest.flaskRest.restRequest.RestRequest')
def test_ctrlM_no_output(self, mock_RestRequest):
""" UAT 4 : ctrl-M job output is not found. """
print("\n\n======================================")
print("test_ctrlM_no_output")
print("======================================")
jobid_no_output = 'blabla:1epyx'
message_noutput = 'Job CTRL-M {} not found'.format(jobid_no_output)
expected_dict = { "blabla:1epyx": {"errors": [{ "message": "Request rejected by Data Center CTM5319 OUTPUT DOES NOT EXIST FOR THIS JOB"}]} }
# mock object :
mock_response = mock.MagicMock()
mock_response.return_value.json = expected_dict
mock_response.return_value.status_code = 500
# # the patched object returns values
# mock_RestRequest.get_response.return_value = mock_response
mock_RestRequest.get_response.return_value.status_code.return_value = 500
mock_RestRequest.get_response.return_value.json.return_value = expected_dict
#mock_RestRequest.return_value.response.return_value.json.return_value = expected_dict
print('$$$$$$$$$$$$$$$$$$$$$$$', mock_RestRequest.mock_calls)
print('$$$$$$$$$$$$$$$$$$$$$$$', mock_RestRequest.get_response.return_value.status_code.return_value)
# REST url to test
url = '/ctrlm/{}/output/'.format(jobid_no_output, follow_redirects=True)
# call
> response_dict = self.client.get(url)
tests\test_mock.py:70:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\virtualenvs\tools\lib\site-packages\werkzeug\test.py:791: in get
return self.open(*args, **kw)
..\..\virtualenvs\tools\lib\site-packages\flask\testing.py:127: in open
follow_redirects=follow_redirects)
..\..\virtualenvs\tools\lib\site-packages\werkzeug\test.py:764: in open
response = self.run_wsgi_app(environ, buffered=buffered)
..\..\virtualenvs\tools\lib\site-packages\werkzeug\test.py:677: in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
..\..\virtualenvs\tools\lib\site-packages\werkzeug\test.py:884: in run_wsgi_app
app_rv = app(environ, start_response)
..\..\virtualenvs\tools\lib\site-packages\flask\app.py:1997: in __call__
return self.wsgi_app(environ, start_response)
..\..\virtualenvs\tools\lib\site-packages\flask\app.py:1985: in wsgi_app
response = self.handle_exception(e)
..\..\virtualenvs\tools\lib\site-packages\flask_restful\__init__.py:273: in error_router
return original_handler(e)
..\..\virtualenvs\tools\lib\site-packages\flask\app.py:1540: in handle_exception
reraise(exc_type, exc_value, tb)
..\..\virtualenvs\tools\lib\site-packages\flask\_compat.py:32: in reraise
raise value.with_traceback(tb)
..\..\virtualenvs\tools\lib\site-packages\flask\app.py:1982: in wsgi_app
response = self.full_dispatch_request()
..\..\virtualenvs\tools\lib\site-packages\flask\app.py:1614: in full_dispatch_request
rv = self.handle_user_exception(e)
..\..\virtualenvs\tools\lib\site-packages\flask_restful\__init__.py:273: in error_router
return original_handler(e)
..\..\virtualenvs\tools\lib\site-packages\flask\app.py:1517: in handle_user_exception
reraise(exc_type, exc_value, tb)
..\..\virtualenvs\tools\lib\site-packages\flask\_compat.py:32: in reraise
raise value.with_traceback(tb)
..\..\virtualenvs\tools\lib\site-packages\flask\app.py:1612: in full_dispatch_request
rv = self.dispatch_request()
..\..\virtualenvs\tools\lib\site-packages\flask\app.py:1598: in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
..\..\virtualenvs\tools\lib\site-packages\flask_restful\__init__.py:484: in wrapper
return self.make_response(data, code, headers=headers)
..\..\virtualenvs\tools\lib\site-packages\flask_restful\__init__.py:513: in make_response
resp = self.representations[mediatype](data, *args, **kwargs)
..\..\virtualenvs\tools\lib\site-packages\flask_restful\representations\json.py:21: in output_json
dumped = dumps(data, **settings) + "\n"
c:\users\x165896\appdata\local\programs\python\python36-32\Lib\json\__init__.py:231: in dumps
return _default_encoder.encode(obj)
c:\users\x165896\appdata\local\programs\python\python36-32\Lib\json\encoder.py:199: in encode
chunks = self.iterencode(o, _one_shot=True)
c:\users\x165896\appdata\local\programs\python\python36-32\Lib\json\encoder.py:257: in iterencode
return _iterencode(o, 0)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <json.encoder.JSONEncoder object at 0x036BC630>
o = <MagicMock name='RestRequest().get_response().status_code' id='81333936'>
def default(self, o):
"""Implement this method in a subclass such that it returns
a serializable object for ``o``, or calls the base implementation
(to raise a ``TypeError``).
For example, to support arbitrary iterators, you could
implement default like this::
def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return JSONEncoder.default(self, o)
"""
raise TypeError("Object of type '%s' is not JSON serializable" %
> o.__class__.__name__)
E TypeError: Object of type 'MagicMock' is not JSON serializable
c:\users\x165896\appdata\local\programs\python\python36-32\Lib\json\encoder.py:180: TypeError
我想,我得到了这个,因为没有使用模拟的返回值。因此,创建了一个模拟对象,当然也不能被代码使用... 在pytest输出中:
o = <MagicMock name='RestRequest().get_response().status_code' id='81333936'>
所以,这意味着我没有嘲笑正确的事情。
我真正想要模拟的是RestRequest实例变量:&#39; response&#39;。这是一个Requests.response对象。
我成功地直接修补了MonAPPflaskRest.flaskRest.restRequest.RestRequest.response&#39;变量。输出听起来不错但assert_called()失败了。所以,给我一些怀疑。
所以,我真的不知道要嘲笑什么,实际上:实例变量&#39;响应&#39;对象&#39; RestRequest&#39;,上下文管理器,....