在 Python 模拟中模拟属性?

在 Python 中使用 mock非常困难:

def method_under_test():
r = requests.post("http://localhost/post")


print r.ok # prints "<MagicMock name='post().ok' id='11111111'>"


if r.ok:
return StartResult()
else:
raise Exception()


class MethodUnderTestTest(TestCase):


def test_method_under_test(self):
with patch('requests.post') as patched_post:
patched_post.return_value.ok = True


result = method_under_test()


self.assertEqual(type(result), StartResult,
"Failed to return a StartResult.")

测试实际上返回了正确的值,但是 r.ok是一个 Mock 对象,而不是 True。如何在 Python 的 mock库中模拟属性?

121735 次浏览

You need to use return_value and PropertyMock:

with patch('requests.post') as patched_post:
type(patched_post.return_value).ok = PropertyMock(return_value=True)

This means: when calling requests.post, on the return value of that call, set a PropertyMock for the property ok to return the value True.

With mock version '1.0.1' the simpler syntax mentioned in the question is supported and works as is!

Example code updated (py.test is used instead of unittest):

import mock
import requests




def method_under_test():
r = requests.post("http://localhost/post")


print r.ok


if r.ok:
return r.ok
else:
raise Exception()




def test_method_under_test():
with mock.patch('requests.post') as patched_post:
patched_post.return_value.ok = True


result = method_under_test()
assert result is True, "mock ok failed"

Run this code with: (make sure you install pytest)

$ py.test -s -v mock_attributes.py
======= test session starts =======================
platform linux2 -- Python 2.7.10 -- py-1.4.30 -- pytest-2.7.2 -- /home/developer/miniconda/bin/python
rootdir: /home/developer/projects/learn/scripts/misc, inifile:
plugins: httpbin, cov
collected 1 items


mock_attributes.py::test_method_under_test True
PASSED


======= 1 passed in 0.03 seconds =================

A compact and simple way to do it is to use new_callable patch's attribute to force patch to use PropertyMock instead of MagicMock to create the mock object. The other arguments passed to patch will be used to create PropertyMock object.

with patch('requests.post.ok', new_callable=PropertyMock, return_value=True) as mock_post:
"""Your test"""