Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2012/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
像urllib这样的模拟/存根python模块如何_Python_Unit Testing_Testing_Mocking - Fatal编程技术网

像urllib这样的模拟/存根python模块如何

像urllib这样的模拟/存根python模块如何,python,unit-testing,testing,mocking,Python,Unit Testing,Testing,Mocking,我需要测试一个函数,该函数需要使用urllib.urlopen(它还使用urllib.urlencode)查询外部服务器上的页面。服务器可能会关闭,页面可能会更改;我不能靠它来考试 控制urllib.urlopen返回内容的最佳方法是什么?处理此问题的最佳方法可能是拆分代码,以便将处理页面内容的逻辑从获取页面的代码中拆分出来 然后将获取程序代码的实例传递到处理逻辑中,然后就可以轻松地用单元测试的模拟获取程序替换它 e、 g 最简单的方法是更改函数,使其不必使用urllib.urlopen。假设这

我需要测试一个函数,该函数需要使用urllib.urlopen(它还使用urllib.urlencode)查询外部服务器上的页面。服务器可能会关闭,页面可能会更改;我不能靠它来考试


控制urllib.urlopen返回内容的最佳方法是什么?

处理此问题的最佳方法可能是拆分代码,以便将处理页面内容的逻辑从获取页面的代码中拆分出来

然后将获取程序代码的实例传递到处理逻辑中,然后就可以轻松地用单元测试的模拟获取程序替换它

e、 g


最简单的方法是更改函数,使其不必使用urllib.urlopen。假设这是您的原始函数:

def my_grabber(arg1, arg2, arg3):
    # .. do some stuff ..
    url = make_url_somehow()
    data = urllib.urlopen(url)
    # .. do something with data ..
    return answer
添加一个参数,该参数是用于打开URL的函数。然后,您可以提供一个模拟函数来执行您需要的任何操作:

def my_grabber(arg1, arg2, arg3, urlopen=urllib.urlopen):
    # .. do some stuff ..
    url = make_url_somehow()
    data = urlopen(url)
    # .. do something with data ..
    return answer

def test_my_grabber():
    my_grabber(arg1, arg2, arg3, urlopen=my_mock_open)
>>> import urllib
>>> # check that it works
>>> urllib.urlopen('http://www.google.com/')
<addinfourl at 3082723820L ...>
>>> # check what happens when it doesn't
>>> urllib.urlopen('http://hopefully.doesnotexist.com/')
#-- snip --
IOError: [Errno socket error] (-2, 'Name or service not known')

>>> # OK, let's mock it up
>>> import mox
>>> m = mox.Mox()
>>> m.StubOutWithMock(urllib, 'urlopen')
>>> # We can be verbose if we want to :)
>>> urllib.urlopen(mox.IgnoreArg()).AndRaise(
...   IOError('socket error', (-2, 'Name or service not known')))

>>> # Let's check if it works
>>> m.ReplayAll()
>>> urllib.urlopen('http://www.google.com/')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/site-packages/mox.py", line 568, in __call__
    raise expected_method._exception
IOError: [Errno socket error] (-2, 'Name or service not known')

>>> # yay! now unset everything
>>> m.UnsetStubs()
>>> m.VerifyAll()
>>> # and check that it still works
>>> urllib.urlopen('http://www.google.com/')
<addinfourl at 3076773548L ...>

另一种简单的方法是让您的测试覆盖urllib的
urlopen()
函数。例如,如果您的模块

import urllib

def some_function_that_uses_urllib():
    ...
    urllib.urlopen()
    ...
您可以这样定义测试:

import mymodule

def dummy_urlopen(url):
    ...

mymodule.urllib.urlopen = dummy_urlopen
然后,当您的测试调用
mymodule
中的函数时,将调用
dummy\u urlopen()
,而不是真正的
urlopen()
。像Python这样的动态语言使得测试方法和类变得非常容易

请参阅我在的博客文章,以了解有关删除测试依赖项的更多信息。

您看了吗?它应该做你需要的一切。下面是一个简单的交互式会话,演示了您需要的解决方案:

def my_grabber(arg1, arg2, arg3, urlopen=urllib.urlopen):
    # .. do some stuff ..
    url = make_url_somehow()
    data = urlopen(url)
    # .. do something with data ..
    return answer

def test_my_grabber():
    my_grabber(arg1, arg2, arg3, urlopen=my_mock_open)
>>> import urllib
>>> # check that it works
>>> urllib.urlopen('http://www.google.com/')
<addinfourl at 3082723820L ...>
>>> # check what happens when it doesn't
>>> urllib.urlopen('http://hopefully.doesnotexist.com/')
#-- snip --
IOError: [Errno socket error] (-2, 'Name or service not known')

>>> # OK, let's mock it up
>>> import mox
>>> m = mox.Mox()
>>> m.StubOutWithMock(urllib, 'urlopen')
>>> # We can be verbose if we want to :)
>>> urllib.urlopen(mox.IgnoreArg()).AndRaise(
...   IOError('socket error', (-2, 'Name or service not known')))

>>> # Let's check if it works
>>> m.ReplayAll()
>>> urllib.urlopen('http://www.google.com/')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/site-packages/mox.py", line 568, in __call__
    raise expected_method._exception
IOError: [Errno socket error] (-2, 'Name or service not known')

>>> # yay! now unset everything
>>> m.UnsetStubs()
>>> m.VerifyAll()
>>> # and check that it still works
>>> urllib.urlopen('http://www.google.com/')
<addinfourl at 3076773548L ...>
导入urllib >>>#检查它是否有效 >>>urllib.urlopen('http://www.google.com/') >>>#检查不发生时会发生什么 >>>urllib.urlopen('http://hopefully.doesnotexist.com/') #--剪断-- IOError:[Errno套接字错误](-2,‘名称或服务未知’) >>>好的,让我们模拟一下 >>>进口混合氧化物 >>>m=mox.mox() >>>m.StubOutWithMock(urllib,'urlopen') >>>#如果愿意,我们可以长篇大论:) >>>urllib.urlopen(mox.IgnoreArg()).AndRaise( …IOError('套接字错误',(-2,'名称或服务未知')) >>>#让我们检查一下它是否有效 >>>m.ReplayAll() >>>urllib.urlopen('http://www.google.com/') 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 文件“/usr/lib/python2.5/site packages/mox.py”,第568行,在调用中__ 引发所需的\u方法。\u异常 IOError:[Errno套接字错误](-2,‘名称或服务未知’) >>>#耶!现在把一切都搞定 >>>m.未设置的存根() >>>m.VerifyAll() >>>#并检查它是否仍然有效 >>>urllib.urlopen('http://www.google.com/') 我正在使用修补程序装饰器:

from mock import patch

[...]

@patch('urllib.urlopen')
def test_foo(self, urlopen_mock):
    urlopen_mock.return_value = MyUrlOpenMock()

如果您甚至不想加载模块:

import sys,types
class MockCallable():
  """ Mocks a function, can be enquired on how many calls it received """
  def __init__(self, result):
    self.result  = result
    self._calls  = []

  def __call__(self, *arguments):
    """Mock callable"""
    self._calls.append(arguments)
    return self.result

  def called(self):
    """docstring for called"""
    return self._calls

class StubModule(types.ModuleType, object):
  """ Uses a stub instead of loading libraries """

  def __init__(self, moduleName):
    self.__name__ = moduleName
    sys.modules[moduleName] = self

  def __repr__(self):
    name  = self.__name__
    mocks = ', '.join(set(dir(self)) - set(['__name__']))
    return "<StubModule: %(name)s; mocks: %(mocks)s>" % locals()

class StubObject(object):
  pass
与FakeWeb的工作方式完全相同。HttpRety在套接字层工作,因此它应该可以拦截任何python http客户端库。它经过了urllib2、httplib2和请求的战斗测试

import urllib2
from httpretty import HTTPretty, httprettified


@httprettified
def test_one():
    HTTPretty.register_uri(HTTPretty.GET, "http://yipit.com/",
                           body="Find the best daily deals")

    fd = urllib2.urlopen('http://yipit.com')
    got = fd.read()
    fd.close()

    assert got == "Find the best daily deals"

加上Clint Miller的答案,要做到这一点,我必须创建一个伪类,实现如下读取方法:

import mymodule

def dummy_urlopen(url):
    ...

mymodule.urllib.urlopen = dummy_urlopen
类伪造URL:
def读取(foo):
返回“{”some:“json_text”}”
然后要删除urllib2.open:

#Stub out urllib2.open。
def dummy_urlopen(foo、bar、baz):
返回FakeURL()
urllib2.urlopen=dummy\u urlopen

我不确定我是否喜欢让测试中的夹具了解配置细节。。。但是,这确实有效。我认为参数化函数没有任何问题。这里不知道urlopen是如何被伪造的,也不知道为什么会被伪造,只知道它可能会发生。事实上,这可能就是典型的“好猴子补丁”的例子。好像要倒下了。还有其他的链接吗,或者现在已经没有了?我已经很长时间没有在博客上发布了,但我还是把它移植到了博客上,以防万一!如果您没有显式地将模拟对象重置回原始值,那么这将模拟测试模块中的所有urlopen实例和模块中的其他类。当然,在这种情况下,我不确定为什么有人想在单元测试中进行网络调用。我建议使用类似于“with patch…”或@patch()的东西,它可以让您更明确地控制要模拟的内容和限制。太糟糕了,在修补模块函数时它不起作用:/(至少不是0.7.2)不是100%真的,如果您在修补之前导入函数,否则修补会自动失败(没有错误,只是没有得到修补:/)这一点很好;当无法找到相关模块时,补丁应该会抛出错误,而不仅仅是无声地失败。这给了我一个错误。夹具“urlopen_mock”没有在2013年找到,这绝对是最好的答案。让我们投票给Falcão的很棒的库吧,伙计们!从Obj-C的角度来看,我在寻找类似Python的东西。我很高兴找到httprety.Close,但import函数实际上不会导入内容。因此,使用from urllib import*…的调用程序将无法获得所需的函数引用:“不鼓励新使用此库。鼓励人们使用与Python 3中可用的unittest.mock库相匹配的函数。”不完全一样,但我想大多数人使用
请求