在同一个测试中模拟对同一个函数的两个不同的响应

New*_*Guy 5 python unit-testing mocking python-2.7

在我之前的问题中,我询问了如何模拟包含requests.get在我的班级中的班级。如果我只打电话一次,提供的答案效果很好requests.get。然而,事实证明我的课程比我做的例子更复杂。

我的班级打了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() 
Run Code Online (Sandbox Code Playgroud)

现在,事实证明我需要创建两个模拟响应。一种用于初始化,一种用于.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
Run Code Online (Sandbox Code Playgroud)

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

Ada*_*nis 7

看起来之前的答案是使用“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})
Run Code Online (Sandbox Code Playgroud)

或者,您可以像这样实现它:

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})
Run Code Online (Sandbox Code Playgroud)

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


che*_*ner 6

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

fake_responses = [Mock(), Mock()]
fake_responses[0].json.return_value = ...
fake_responses[1].json.return_value = ...
fake_get.side_effects = fake_responses
Run Code Online (Sandbox Code Playgroud)