2008-11-17 12 views

Odpowiedz

88

Innym prostym rozwiązaniem jest mieć urlopen() funkcję test override urllib za. Na przykład, jeśli moduł ma

import urllib 

def some_function_that_uses_urllib(): 
    ... 
    urllib.urlopen() 
    ... 

Można zdefiniować test tak:

import mymodule 

def dummy_urlopen(url): 
    ... 

mymodule.urllib.urlopen = dummy_urlopen 

Następnie, gdy testy wywoływać funkcje w mymodule, dummy_urlopen() będzie wywoływana zamiast realnej urlopen(). Języki dynamiczne, takie jak Python, bardzo ułatwiają usuwanie metod i klas do testowania.

Zobacz moje posty na blogu pod numerem http://softwarecorner.wordpress.com/, aby uzyskać więcej informacji o ograniczaniu zależności dla testów.

+11

Monkeypatches do testowania to przydatna rzecz. Rzeczywiście jest to prawdopodobnie kanoniczny przykład "dobrego monkeypatch". –

+0

http://visionandexecution.org wydaje się być wyłączony. Czy jest inny link, czy to już nie ma? –

+1

Nie pisałem na blogu od bardzo dawna, ale zrobiłem to na http://softwarecorner.wordpress.com/ –

8

Prawdopodobnie najlepszym sposobem na poradzenie sobie z tym jest podział kodu, aby logika, która przetwarza treść strony, została podzielona z kodu pobierającego stronę.

Następnie prześlij instancję kodu pobierania do logiki przetwarzania, a następnie z łatwością wymień ją na próbny moduł pobierania dla testu urządzenia.

np.

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 
3

Najprostszy sposób to zmienić funkcję, aby nie musiała korzystać z urllib.urlopen. Załóżmy, że jest to twoja pierwotna funkcja:

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

Dodaj argument, który jest funkcją do otwarcia adresu URL. Następnie można dostarczyć makiety funkcję robić, co trzeba:

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) 
+3

Nie jestem pewien, czy podoba mi się testowanie urządzenia pod kątem szczegółów konfiguracji ... Jednak to działa. –

+1

Nie widzę niczego złego w parametryzowaniu funkcji. Nie ma tu żadnej wiedzy na temat tego, jak urlop może zostać sfałszowany lub dlaczego, po prostu dlatego, że może się zdarzyć. –

27

Czy podałeś Mox wygląd? Powinno zrobić wszystko, czego potrzebujesz. Oto prosty interaktywna sesja ilustrujący rozwiązanie, czego potrzebujesz:

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

Używam Mock's poprawki dekorator:

from mock import patch 

[...] 

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

Szkoda, że ​​nie działa, gdy łata funkcje modułu:/(przynajmniej nie 0.7.2) –

+2

nie jest w 100% prawdziwe, jeśli zaimportujesz funkcję przed łataniem działa, w przeciwnym razie łatanie nie powiedzie się po cichu (brak błędów, nic się nie załatało : /) –

+2

Dobra uwaga; łatanie powinno powodować błędy, gdy nie udało się znaleźć odpowiedniego modułu, a nie po prostu cicho. – fatuhoku

7

W przypadku, gdy nie chcą nawet załadować moduł:

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 

A potem:

>>> 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' 
+0

Zamknij, ale funkcja importowania nie będzie faktycznie importować rzeczy. Tak więc dzwoniący korzystający z importu urllib * ... nie uzyska wymaganych funkcji. –

13

HTTPretty działa w taki sam sposób, jak robi to FakeWeb. HTTPretty działa w warstwie gniazd, więc powinien działać przechwytując dowolne biblioteki klienta HTTP Pythona. Jest testowany pod kątem bitwy pod adresem urllib2, httplib2 i próśb

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" 
+0

W 2013 r. Jest to zdecydowanie najlepsza odpowiedź. Zagrajmy w niesamowitą bibliotekę Falcão! – fatuhoku

+0

Pochodzę z kąta Obj-C, szukałem czegoś takiego jak [OHHTTPStubs] (https://github.com/AliSoftware/OHHTTPStubs) dla Pythona. Cieszę się, że mogę znaleźć HTTPretty. – fatuhoku

Powiązane problemy