如何创建带返回类属性方法的mock.Mock对象实现requests.get打桩
问题背景
我希望对requests.get进行patch打桩,让测试逻辑不依赖网络连接,要求模拟方法返回携带合法json数据与状态码的Response对象。我最初编写的模拟函数中定义了包含全部所需能力的MockResponse类(即测试代码中被注释的部分),该方案可正常运行、符合预期。
但pylint提示该类仅包含一个公共方法,我尝试通过mock.Mock创建模拟对象来消除该警告,却始终无法正常运行,核心疑问是:是否可以不定义这类小型自定义类就完成该模拟逻辑?
补充说明:
- 后续Waket Zheng优化了WebTools代码,新增了属性方法
ok需要模拟,此时自定义MockResponse类包含2个方法,pylint警告已不再出现。 - TLDR:问题下的现有编辑内容提供了一种替代方案,但并未解答我最初提出的核心问题。
相关代码
测试代码
class TestWebtools(TestCase): VALID_TEST_CASES = [ ('https://testurl.com/simple.json', {"value": "someValue"}, "someValue"), ('https://testurl.com/valueDoesNotExist.json', {}, ""), ('https://testurl.com/error1', None, ""), ] def _mocked_requests_get(*args, **_): # class MockResponse: # def __init__(self, json_data, status_code): # self.json_data = json_data # self.status_code = status_code # # def json(self): # return self.json_data # # @property # def ok(self): # return self.status_code == 200 MockResponse = mock.Mock() MockResponse.json.return_value = MockResponse.json_data for url, value, _ in TestWebtools.VALID_TEST_CASES: if args[0] == url: return MockResponse(json_data=value, status_code=200) return MockResponse(json_data=None, status_code=404) @mock.patch('requests.get', _mocked_requests_get) def test_download_json(self): for url, expected, _ in self.VALID_TEST_CASES: self.assertEqual(expected, download_json(url))
被测WebTools代码
def download_json(fromUrl: str) -> Optional[dict]: response = requests.get(fromUrl, timeout=5) if response.ok: return response.json() return None
解决方案
完全可以不定义自定义类,直接用mock.Mock实现模拟逻辑。之前的写法无法运行的核心原因是:MockResponse = mock.Mock()创建的是一个Mock实例,不是Mock类,给这个外层实例提前设置的json.return_value属性,不会自动绑定到调用该实例时返回的新子Mock对象上,同时也没有配置ok属性的返回值,自然会运行失败。
正确的实现方式是,每次匹配到请求url时,直接创建新的Mock响应实例,单独给该实例配置需要的属性和方法即可,示例代码如下:
def _mocked_requests_get(*args, **_): for url, value, _ in TestWebtools.VALID_TEST_CASES: if args[0] == url: mock_resp = mock.Mock(status_code=200) mock_resp.json.return_value = value mock_resp.ok = True return mock_resp # 匹配不到url时返回404响应 mock_resp = mock.Mock(status_code=404) mock_resp.json.return_value = None mock_resp.ok = False return mock_resp
如果希望ok属性和status_code自动联动,和真实requests.Response逻辑保持一致,可以用PropertyMock实现动态属性计算,不需要手动指定ok的布尔值:
def _mocked_requests_get(*args, **_): for url, value, _ in TestWebtools.VALID_TEST_CASES: if args[0] == url: mock_resp = mock.Mock(status_code=200) mock_resp.json.return_value = value # ok属性随status_code动态变化 type(mock_resp).ok = mock.PropertyMock(side_effect=lambda: mock_resp.status_code == 200) return mock_resp mock_resp = mock.Mock(status_code=404) mock_resp.json.return_value = None type(mock_resp).ok = mock.PropertyMock(side_effect=lambda: mock_resp.status_code == 200) return mock_resp
以上两种写法都不需要自定义类,完全满足测试需求,也不会触发pylint的相关警告。
内容的提问来源于stack exchange,提问作者lukstru
相关产品推荐
相关产品推荐

