当前位置:  开发笔记 > 编程语言 > 正文

一个mock/stub python模块如何像urllib一样

如何解决《一个mock/stubpython模块如何像urllib一样》经验,为你挑选了6个好方法。

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

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



1> Clint Miller..:

另一个简单的方法是让你的测试覆盖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_urlopen()将调用而不是真实的urlopen().像Python这样的动态语言使得用于测试的方法和类非常容易.

有关存根测试依赖关系的更多信息,请参阅http://softwarecorner.wordpress.com/上的博客文章.


谨防!如果您没有将模拟对象显式重置为原始值,这将模拟测试模块中的urlopen和模块中的其他类的所有实例.在这种情况下,我不确定为什么有人会想在单元测试中进行网络调用.我建议使用像'with patch ...'或@patch()这样的东西,它可以让你更明确地控制你正在嘲笑的东西和最多的限制.
用于测试的Monkeypatches是一个方便的东西.实际上,这可能是规范的"好的monkeypatch"例子.

2> Dinoboff..:

我正在使用Mock的补丁装饰器:

from mock import patch

[...]

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


太糟糕了,修补模块功能时不起作用:/(至少不是0.7.2)
不是100%是真的,如果你在修补它之前导入了它的功能,否则补丁会无声地失败(没有错误,没有任何补丁:/)
那里好点; 补丁应该在找不到相关模块时抛出错误而不是静默失败.

3> Damir Zekić..:

你有没有看过Mox?它应该做你需要的一切.这是一个简单的交互式会话,说明您需要的解决方案:

>>> import urllib
>>> # check that it works
>>> urllib.urlopen('http://www.google.com/')

>>> # 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 "", line 1, in 
  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/')



4> Gabriel Falc..:

HTTPretty的工作方式与FakeWeb完全相同.HTTPretty在套接字层中工作,因此它应该可以拦截任何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"



5> Douglas Leed..:

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

然后将fetcher代码的实例传递给处理逻辑,然后您可以使用模拟获取器轻松替换它以进行单元测试.

例如

class Processor(oject):
    def __init__(self, fetcher):
        self.m_fetcher = fetcher

    def doProcessing(self):
        ## use self.m_fetcher to get page contents

class RealFetcher(object):
    def fetchPage(self, url):
        ## get real contents

class FakeFetcher(object):
    def fetchPage(self, url):
        ## Return whatever fake contents are required for this test



6> 小智..:

如果您不想加载模块:

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 "" % locals()

class StubObject(object):
  pass

然后:

>>> urllib = StubModule("urllib")
>>> import urllib # won't actually load urllib

>>> urls.urlopen = MockCallable(StubObject())

>>> example = urllib.urlopen('http://example.com')
>>> example.read = MockCallable('foo')

>>> print(example.read())
'foo'

推荐阅读
吻过彩虹的脸_378
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有