简体   繁体   English

如何使用 pytest-mock 监视 function

[英]How to use pytest-mock to spy on function

I have a class in foo.py that I wish to test:我在 foo.py 中有一个 class 我想测试:

import requests

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

I want to replace the requests call to simulate the response.我想替换 requests 调用来模拟响应。

Here is my test file in test_foo.py:这是我在 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

I want to check that the mocked_requests_get function is called only once.我想检查 mocked_requests_get function 是否只被调用一次。

The interpreter gives an error on the spy = mocker.spy... line: 'Foo' object has no attribute 'test_foo.mocked_requests_get'解释器在 spy = mocker.spy... 行给出错误:'Foo' object has no attribute 'test_foo.mocked_requests_get'

This is understandable - but I can't work out a way to get to the object instance that references that function.这是可以理解的——但我无法找到引用 function 的 object 实例的方法。 Can anyone help please?有人可以帮忙吗?

Your approach is a bit too complicated - you can just use a standard mock without the need to implement your own mock class.您的方法有点太复杂了——您可以只使用标准模拟,而无需实现自己的模拟 class。 Something like this should work:像这样的东西应该工作:

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

Also note that you have to patch requests.get instead of foo.requests.get , because you import requests directly (see where to patch ).另请注意,您必须修补requests.get而不是foo.requests.get ,因为您直接导入requests (请参阅修补位置)。

UPDATE:更新:
If you need the response to depend on the url, as shown in the updated question, you could use side_effect , which can take a function or class object (unless return_value , which needs an evaluated value): If you need the response to depend on the url, as shown in the updated question, you could use side_effect , which can take a function or class object (unless return_value , which needs an evaluated 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM