【问题标题】:How to use pytest-mock to mock a function conditionally如何使用 pytest-mock 有条件地模拟函数
【发布时间】:2021-02-26 13:55:58
【问题描述】:

我想测试 foo.py 的一门课程:

import requests

class Foo:
    def fooMethod(self, url):
        response = requests.get(url)
        return response

我想替换requests调用来模拟响应。

这是我在test_foo.py中的测试文件:

from foo import Foo

def mocked_requests_get(*args, **kwargs):
    class MockResponse:
        def __init__(self, text, code):
            self.text = text
            self.code = code
    
    if args[0] == "http://localhost":
        return MockResponse("Test response", 200)

    return MockResponse(None, 404)


class TestFoo:
    def test_foo(self, mocker):
        a = Foo()
        mocker.patch ('foo.requests.get', mocked_requests_get)
        spy = mocker.spy (a, 'test_foo.mocked_requests_get')
        response = a.fooMethod("http://localhost")
        assert response.text == "Test response"
        assert spy.call_count == 1

我想检查mocked_requests_get 函数是否只被调用过一次。

解释器在spy = mocker.spy ... 行给出错误:

'Foo' object has no attribute 'test_foo.mocked_requests_get'

这是可以理解的 - 但我无法找到一种方法来获取引用该函数的对象实例。有人可以帮忙吗?

【问题讨论】:

    标签: python pytest pytest-mock


    【解决方案1】:

    您的方法有点太复杂了——您可以只使用标准模拟,而无需实现自己的模拟类。像这样的东西应该可以工作:

    class TestFoo:
        def test_foo(self, mocker):
            a = Foo()
            get_mock = mocker.patch('requests.get')
            get_mock.return_value.text = "Test response"
            response = a.fooMethod()
            assert response.text == "Test response"
            assert get_mock.call_count == 1
    

    还请注意,您必须修补requests.get 而不是foo.requests.get,因为您直接导入requests(请参阅where to patch)。

    更新:
    如果您需要响应依赖于 url,如更新后的问题所示,您可以使用side_effect,它可以采用函数或类对象(除非return_value,它需要评估值):

    class MockedResponse:
        def __init__(self, url):
            self.url = url
    
        @property
        def text(self):
            responses = {"url1": "response1",
                         "url2": "response2",
                         "url3": "response3"}
            if self.url in responses:
                return responses[self.url]
            return "default"
    
    
    class TestFoo:
        def test_foo(self, mocker):
            a = Foo()
            get_mock = mocker.patch('requests.get')
            get_mock.side_effect = MockedResponse
            response = a.fooMethod("url2")
            assert response.text == "response2"
            assert get_mock.call_count == 1
    

    【讨论】:

    • 感谢您的回复-您的回答确实有效,但我只给了您故事的一部分。我希望能够根据调用的 url 返回不同的值。所以我在这里遵循这个例子:[link]stackoverflow.com/questions/15753390/… 请问有什么方法可以实现吗?
    • 如果您可以相应地编辑问题会有所帮助。 URL 是 fooMethod 的参数吗?
    • 我已经更新了问题。感谢您的回复 - 我想我现在快到了,但我收到一个错误:失败:TypeError:MockedResponse()不接受任何参数。任何想法我做错了什么?
    • 道歉 - 我有__init_ 而不是__init__ 非常感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2016-11-04
    • 1970-01-01
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    • 2019-12-15
    • 2018-12-21
    • 1970-01-01
    相关资源
    最近更新 更多