has*_*sam 9 python python-3.x python-mock python-unittest python-unittest.mock
例如,我有一些foo.py带有以下代码的 module( ):
import requests
def get_ip():
return requests.get('http://jsonip.com/').content
Run Code Online (Sandbox Code Playgroud)
和bar.py具有类似代码的模块:
import requests
def get_fb():
return requests.get('https://fb.com/').content
Run Code Online (Sandbox Code Playgroud)
我只是不明白为什么接下来会发生:
from mock import patch
from foo import get_ip
from bar import get_fb
with patch('foo.requests.get'):
print(get_ip())
print(get_fb())
Run Code Online (Sandbox Code Playgroud)
他们两个被嘲笑:
<MagicMock name='get().content' id='4352254472'>
<MagicMock name='get().content' id='4352254472'>
似乎只修补foo.get_ip方法with patch('foo.requests.get'),但事实并非如此。我知道我可能会bar.get_fb超出with范围进行调用,但在某些情况下,我只在上下文管理器中运行一种调用许多其他方法的方法,并且我只想requests在一个模块中进行修补。有什么办法可以解决这个问题吗?不改变模块中的导入
这两个位置foo.requests.get引用bar.requests.get同一个对象,因此在一个地方模拟它,然后在另一个地方模拟它。
想象一下您如何实施补丁。您必须找到符号所在的位置并将该符号替换为模拟对象。退出 with 上下文时,您将需要恢复符号的原始值。像(未经测试)的东西:
class patch(object):
def __init__(self, symbol):
# separate path to container from name being mocked
parts = symbol.split('.')
self.path = '.'.join(parts[:-1]
self.name = parts[-1]
def __enter__(self):
self.container = ... lookup object referred to by self.path ...
self.save = getattr(self.container, name)
setattr(self.container, name, MagicMock())
def __exit__(self):
setattr(self.container, name, self.save)
Run Code Online (Sandbox Code Playgroud)
所以你的问题是你正在模拟请求模块中的对象,然后你从 foo 和 bar 引用该对象。
按照 @elethan 的建议,您可以模拟 foo 中的 requests 模块,甚至对 get 方法提供副作用:
from unittest import mock
import requests
from foo import get_ip
from bar import get_fb
def fake_get(*args, **kw):
print("calling get with", args, kw)
return mock.DEFAULT
replacement = mock.MagicMock(requests)
replacement.get = mock.Mock(requests.get, side_effect=fake_get, wraps=requests.get)
with mock.patch('foo.requests', new=replacement):
print(get_ip())
print(get_fb())
Run Code Online (Sandbox Code Playgroud)
更直接的解决方案是改变您的代码,以便foo将bar引用get直接拉到它们的名称空间中。
foo.py:
from requests import get
def get_ip():
return get('http://jsonip.com/').content
Run Code Online (Sandbox Code Playgroud)
酒吧.py:
from requests import get
def get_ip():
return get('https://fb.com/').content
Run Code Online (Sandbox Code Playgroud)
主要.py:
from mock import patch
from foo import get_ip
from bar import get_fb
with patch('foo.get'):
print(get_ip())
print(get_fb())
Run Code Online (Sandbox Code Playgroud)
生产:
<MagicMock name='get().content' id='4350500992'>
b'<!DOCTYPE html>\n<html lang="en" id="facebook" ...
Run Code Online (Sandbox Code Playgroud)
更新了更完整的解释,以及更好的解决方案(2016-10-15)
注意:添加了wraps=requests.get副作用后调用底层函数的功能。
| 归档时间: |
|
| 查看次数: |
3163 次 |
| 最近记录: |