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

76 投票
8 回答
56269 浏览
提问于 2025-04-11 19:29

我需要测试一个函数,这个函数需要用urllib.urlopen去查询一个外部服务器上的页面(它还用到了urllib.urlencode)。但是,那个服务器可能会宕机,页面也可能会改变;所以我不能依赖它来进行测试。

那么,有什么好的方法可以控制urllib.urlopen返回的结果呢?

8 个回答

28

你有没有看看Mox这个工具?它应该能满足你所有的需求。下面是一个简单的互动示例,展示了你需要的解决方案:

>>> 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 ...>
74

我正在使用Mock库中的patch装饰器:

from mock import patch

[...]

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

另一种简单的方法是让你的测试覆盖掉 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/

撰写回答