如何使用 pytest-mock 有条件地模拟一个函数
How to use pytest-mock to mock a function conditionally
我想测试 foo.py
中的 class:
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'
这是可以理解的 - 但我想不出一种方法来获取引用该函数的对象实例。有人可以帮忙吗?
您的方法有点太复杂了 - 您可以只使用标准模拟而不需要实现您自己的模拟 class。这样的事情应该有效:
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
,它可以采用函数或 class 对象(除非 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
我想测试 foo.py
中的 class:
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'
这是可以理解的 - 但我想不出一种方法来获取引用该函数的对象实例。有人可以帮忙吗?
您的方法有点太复杂了 - 您可以只使用标准模拟而不需要实现您自己的模拟 class。这样的事情应该有效:
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
,它可以采用函数或 class 对象(除非 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