Как можно издеваться / заглушка python модуль, как urllib
Мне нужно проверить функцию, которая должна запрашивать страницу на внешнем сервере с помощью urllib.urlopen (он также использует urllib.urlencode). Сервер может быть отключен, страница может измениться; я не могу полагаться на него для теста.
каков наилучший способ контролировать то, что urllib.urlopen возвращается?
7 ответов:
Я использую макет-это патч оформитель:
from mock import patch [...] @patch('urllib.urlopen') def test_foo(self, urlopen_mock): urlopen_mock.return_value = MyUrlOpenMock()
ты мох посмотреть? Он должен делать все, что вам нужно. Вот простой интерактивный сеанс, иллюстрирующий решение, которое вам нужно:
>>> 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 ...>
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"
вероятно, лучший способ справиться с этим-разделить код, чтобы логика, обрабатывающая содержимое страницы, была отделена от кода, который извлекает страницу.
затем передайте экземпляр кода fetcher в логику обработки, затем вы можете легко заменить его на макет 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
в случае, если вы не хотите, чтобы даже загрузить модуль:
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и затем:
>>> 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'
самый простой способ-изменить свою функцию так, чтобы она не обязательно использовала urllib.urlоткрыть. Допустим, это ваша исходная функция:
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)
Comments