Python 中的模拟嵌套方法
Mock nested method in Python
为此绞尽脑汁。我想模拟下面的生成器方法 self.api.redditor(username).comments.new(limit=num)
和 self.api.redditor(username).submissions.new(limit=num)
,其中 self.api
被分配给 class 实例,如 self.api = PrawReddit()
我正在尝试测试结果的大小:self.assertEqual(len(result), 5)
到目前为止,我尝试了 MockPraw.return_value.redditor.return_value.comments.return_value.new.return_value.__iter__.return_value = iter(['c' * 10])
但测试失败 AssertionError: 0 != 5
非常感谢任何提示。
def get_comments_submissions(self, username, num=5):
"""Return max `num` of comments and submissions by `username`."""
coms = [
dict(
title=comment.link_title,
text=comment.body_html,
subreddit=comment.subreddit_name_prefixed,
url=comment.link_url,
created=datetime.fromtimestamp(comment.created_utc, pytz.utc),
)
for comment in self.api.redditor(username).comments.new(limit=num)
]
subs = [
dict(
title=submission.title,
text=submission.selftext_html,
subreddit=submission.subreddit_name_prefixed,
url=submission.url,
created=datetime.fromtimestamp(submission.created_utc, pytz.utc),
)
for submission in self.api.redditor(username).submissions.new(limit=num)
]
return coms + subs if len(coms + subs) < num else (coms + subs)[:num]
要模拟生成器(除非您使用特定的生成器功能),您可以使用迭代器作为替代,例如
import unittest.mock as mock
generator_mock = Mock(return_value=iter(("foo", "bar")))
当你有像你的例子中那样的嵌套结构时,这会变得有点复杂,属性访问会自动处理,但必须定义来自函数的 return_value
。从你的例子:
# API mock
mock_api = Mock()
mock_api.redditor.return_value = mock_subs = Mock()
# Submissions mock
mock_subs.new.return_value = iter(("foo", "bar"))
然后可以调用和断言
for item in mock_api.api.redditor("user").submissions.new(limit=5):
print(item)
mock_api.redditor.assert_called_with("user")
mock_subs.new.assert_called_with(limit=5)
由于 API 是同一个 class 的成员,因此必须对其进行猴子修补,例如:
target = Praw()
target.api = mock_api()
target.get_comments_submissions("user")
mock_api.redditor.assert_called_with("user")
mock_subs.new.assert_called_with(limit=5)
请注意 return 值中的 iterator
是单个实例,第二次调用获取迭代器将 return 相同的实例。
像使用 pytest-mock
一样编写,一切都发生在 mymodule
中(您在模块顶部导入了 class,如 from xy import PrawReddit
):
mocker.patch("datetime.fromtimestamp")
mocked_comment = mocker.MagicMock()
mocked_submission = mocker.MagicMock()
mocked = mocker.patch("mymodule.PrawReddit")
mocked.return_value.redditor.return_value.comments.new.return_value = [mocker.MagicMock(), mocked_comment]
mocked.return_value.redditor.return_value.submisions.new.return_value = [mocker.MagicMock(), mocked_submission]
returned = instance.get_comments_submissions("foo", num=2)
assert mocked.return_value.redditor.call_count = 2
mocked.return_value.assert_called_with("foo")
assert returned[-1]["link_title"] == mocked_comment.link_title
具有相同介绍的另一个测试电话:
# ...
returned = instance.get_comments_submissions("foo")
assert mocked.return_value.redditor.call_count = 2
mocked.return_value.assert_called_with("foo")
assert returned[1]["link_title"] == mocked_comment.link_title
assert returned[-1]["title"] == mocked_submission.title
为此绞尽脑汁。我想模拟下面的生成器方法 self.api.redditor(username).comments.new(limit=num)
和 self.api.redditor(username).submissions.new(limit=num)
,其中 self.api
被分配给 class 实例,如 self.api = PrawReddit()
我正在尝试测试结果的大小:self.assertEqual(len(result), 5)
到目前为止,我尝试了 MockPraw.return_value.redditor.return_value.comments.return_value.new.return_value.__iter__.return_value = iter(['c' * 10])
但测试失败 AssertionError: 0 != 5
非常感谢任何提示。
def get_comments_submissions(self, username, num=5):
"""Return max `num` of comments and submissions by `username`."""
coms = [
dict(
title=comment.link_title,
text=comment.body_html,
subreddit=comment.subreddit_name_prefixed,
url=comment.link_url,
created=datetime.fromtimestamp(comment.created_utc, pytz.utc),
)
for comment in self.api.redditor(username).comments.new(limit=num)
]
subs = [
dict(
title=submission.title,
text=submission.selftext_html,
subreddit=submission.subreddit_name_prefixed,
url=submission.url,
created=datetime.fromtimestamp(submission.created_utc, pytz.utc),
)
for submission in self.api.redditor(username).submissions.new(limit=num)
]
return coms + subs if len(coms + subs) < num else (coms + subs)[:num]
要模拟生成器(除非您使用特定的生成器功能),您可以使用迭代器作为替代,例如
import unittest.mock as mock
generator_mock = Mock(return_value=iter(("foo", "bar")))
当你有像你的例子中那样的嵌套结构时,这会变得有点复杂,属性访问会自动处理,但必须定义来自函数的 return_value
。从你的例子:
# API mock
mock_api = Mock()
mock_api.redditor.return_value = mock_subs = Mock()
# Submissions mock
mock_subs.new.return_value = iter(("foo", "bar"))
然后可以调用和断言
for item in mock_api.api.redditor("user").submissions.new(limit=5):
print(item)
mock_api.redditor.assert_called_with("user")
mock_subs.new.assert_called_with(limit=5)
由于 API 是同一个 class 的成员,因此必须对其进行猴子修补,例如:
target = Praw()
target.api = mock_api()
target.get_comments_submissions("user")
mock_api.redditor.assert_called_with("user")
mock_subs.new.assert_called_with(limit=5)
请注意 return 值中的 iterator
是单个实例,第二次调用获取迭代器将 return 相同的实例。
像使用 pytest-mock
一样编写,一切都发生在 mymodule
中(您在模块顶部导入了 class,如 from xy import PrawReddit
):
mocker.patch("datetime.fromtimestamp")
mocked_comment = mocker.MagicMock()
mocked_submission = mocker.MagicMock()
mocked = mocker.patch("mymodule.PrawReddit")
mocked.return_value.redditor.return_value.comments.new.return_value = [mocker.MagicMock(), mocked_comment]
mocked.return_value.redditor.return_value.submisions.new.return_value = [mocker.MagicMock(), mocked_submission]
returned = instance.get_comments_submissions("foo", num=2)
assert mocked.return_value.redditor.call_count = 2
mocked.return_value.assert_called_with("foo")
assert returned[-1]["link_title"] == mocked_comment.link_title
具有相同介绍的另一个测试电话:
# ...
returned = instance.get_comments_submissions("foo")
assert mocked.return_value.redditor.call_count = 2
mocked.return_value.assert_called_with("foo")
assert returned[1]["link_title"] == mocked_comment.link_title
assert returned[-1]["title"] == mocked_submission.title