在同一测试中模拟对相同功能的两个单独响应

时间:2016-03-02 03:10:59

标签: python python-2.7 unit-testing mocking

在我的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的这两个单独调用创建单独的响应?

2 个答案:

答案 0 :(得分:3)

您可以将iterable分配给模拟对象的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

答案 1 :(得分: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