【问题标题】:Mock two separate responses to same function in same test在同一测试中模拟对同一功能的两个单独响应
【发布时间】:2016-06-14 17:58:47
【问题描述】:

在我的previous 问题中,我询问了如何在我的班级中模拟一个包装requests.get 的班级。如果我只调用一次requests.get,则提供的answer 效果很好。然而,事实证明我的课程比我的例子更复杂。

我的班级两次致电request.get。一次在初始化时,因为它到达了一个返回 API 值的 API 端点,我需要在我的实际请求中使用,一次在我进行 .fetch 调用时。

import requests
class ExampleAPI(object):
    def __init__(self):
        self.important_tokens = requests.get(url_to_tokens)['tokens']

    def fetch(self, url, params=None, key=None, token=None, **kwargs):
        return requests.get(url, params=self.important_tokens).json() 

现在,我需要创建两个模拟响应。一个用于初始化,一个用于.fetch。使用上一个答案中的代码:

@patch('mymodule.requests.get')
def test_fetch(self, fake_get):
    expected = {"result": "True"}
    fake_get.return_value.json.return_value = expected
    e = ExampleAPI()    # This needs one set of mocked responses
    self.assertEqual(e.fetch('http://my.api.url.example.com'), expected)    # This needs a second set

我如何为这两个对request.get 的单独调用创建单独的响应?

【问题讨论】:

    标签: python python-2.7 unit-testing mocking


    【解决方案1】:

    看起来之前的答案是使用“side_effects”而不是“side_effect”。这就是您在 Python 3 中可以做到的方式:

    import requests
    import unittest
    from unittest import mock
    from unittest.mock import Mock
    
    
    class Tests(unittest.TestCase):
    
        @mock.patch('requests.get')
        def test_post_price_band(self, fake_get):
            fake_responses = [Mock(), Mock()]
            fake_responses[0].json.return_value = {"a": 1}
            fake_responses[1].json.return_value = {"b": 2}
            fake_get.side_effect = fake_responses
    
            r1 = requests.get('https://www.api.com').json()
            self.assertEqual(r1, {"a": 1})
    
            r2 = requests.get('https://www.api.com').json()
            self.assertEqual(r2, {"b": 2})
    

    或者你可以像这样实现它:

    class MockResponse:
        def __init__(self, json_data, status_code=requests.codes.ok):
            self.json_data = json_data
            self.status_code = status_code
    
        def json(self):
            return self.json_data
    
    
    class Tests(unittest.TestCase):
    
        @mock.patch('requests.get')
        def test_post_price_band(self, fake_get):
            fake_get.side_effect = [
                MockResponse({"a": 1}),
                MockResponse({"b": 2})
            ]
    
            r1 = requests.get('https://www.api.com')
            self.assertEqual(r1.status_code, requests.codes.ok)
            self.assertEqual(r1.json(), {"a": 1})
    
            r2 = requests.get('https://www.api.com')
            self.assertEqual(r2.status_code, requests.codes.ok)
            self.assertEqual(r2.json(), {"b": 2})
    

    还可以查看此库以帮助您:https://github.com/getsentry/responses

    【讨论】:

      【解决方案2】:

      你可以给模拟对象的side_effects 属性分配一个可迭代对象;每次调用 mock 时,它都会返回 iterable 的下一项。

      fake_responses = [Mock(), Mock()]
      fake_responses[0].json.return_value = ...
      fake_responses[1].json.return_value = ...
      fake_get.side_effects = fake_responses
      

      【讨论】:

      • 这似乎是在传递 <MagicMock name='fetch()' id='46383904'> 而不是返回值,即使 fake_responses[0].json.return_value 具有正确的值。
      • @NewGuy 我也有同样的问题,你是怎么解决你回复中的 MagicMock 的?
      • 我遇到了和上面一样的问题。有人设法解决了这个问题吗?
      猜你喜欢
      • 2019-12-14
      • 1970-01-01
      • 1970-01-01
      • 2022-11-14
      • 2018-07-25
      • 2021-10-01
      • 2019-09-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多