2008-11-17 14 views
64

Bir URL'yi urllib.urlopen kullanarak harici bir sunucuda sorgulaması gereken bir işlevi test etmem gerekir (ayrıca urllib.urlencode kullanır). Sunucu kapalı olabilir, sayfa değişebilir; Bir test için ona güvenemem.Bir mock/stub python modülü urllib gibi nasıl olabilir

Hangi urllib.urlopen döndürdüğünü kontrol etmenin en iyi yolu nedir?

cevap

88

Başka basit bir yaklaşım test geçersiz kılma urllib en urlopen() fonksiyonunu sahip olmaktır. Testlerinizi mymodule işlevleri çağırmak ne zaman

Sonra
import mymodule 

def dummy_urlopen(url): 
    ... 

mymodule.urllib.urlopen = dummy_urlopen 

, dummy_urlopen() yerine gerçek urlopen() arasında adı verilecek: Örneğin, modül Böyle testinizi tanımlayabiliriz

import urllib 

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

varsa. Python gibi dinamik diller, test için yöntemleri ve sınıfları saptamayı çok kolaylaştırır.

Testler için bağımlılık sapma konusunda daha fazla bilgi için http://softwarecorner.wordpress.com/ adresindeki blog yazılarıma bakın.

+11

Test için Monkeypatches kullanışlı bir şeydir. Gerçekten de, bu muhtemelen kanonik "iyi maymun" örneğidir. –

+0

http://visionandexecution.org aşağı görünüyor. Başka bir bağlantı var mı, yoksa şimdi mi gitti? –

+1

Gerçekten çok uzun bir süredir blogda yayınlamamıştım, ancak http://softwarecorner.wordpress.com/ adresine bağlantı verdim. –

8

Muhtemelen, bunun üstesinden gelmenin en iyi yolu kodu ayırmaktır, böylece sayfa içeriğini işleyen mantık, sayfayı getiren koddan ayrılır.

Daha sonra, toplayıcı kodunun bir örneğini işlem mantığına geçirin, ardından birim testi için sahte bir getirici ile kolayca değiştirebilirsiniz.

örn.

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

En basit yol, işlevinizi değiştirerek urllib.urlopen kullanılmasını gerektirmeyecek şekilde değiştirmektir. en bu orijinal işlevi olduğunu varsayalım:

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

fonksiyon URL açmak için kullanmaktır bir argüman ekleyin. O zaman ne gerekiyorsa yapmaya sahte işlevi sağlayabilir:

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

'a karşı test edildi. Yapılandırmanın fikstürü yapılandırma ayrıntılarının farkında olduğundan emin değilim ... Ancak, bu işe yarıyor. –

+1

Fonksiyonu parametrelemekte yanlış bir şey göremiyorum. Burada, ütopyanın nasıl sahte olabileceği ya da neden olabileceği hakkında hiçbir bilgi yoktur. –

27

Mox bir bakış verdiniz mi? İhtiyacın olan herşeyi yapmalı. Ben Mock's yama dekoratör kullanıyorum

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

: Burada ihtiyacınız olan çözümü gösteren basit bir interaktif seansı

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 
: hatta modülü yüklemek istemiyorum durumda
from mock import patch 

[...] 

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

çok kötü, modül işlevlerini eklerken işe yaramıyor:/(en az değil 0.7.2) –

+2

% 100 doğru değil, yamayı çalıştırmadan önce işlevi içe aktarırsanız, aksi takdirde yama sessizce başarısız olur (hata yok, yalnızca hiçbir şey yandı : /) –

+2

İyi nokta var; yama, sadece sessizce başarısız olmak yerine ilgili modülü bulamadığı zaman hata atmalıdır. – fatuhoku

7

sonra

Ve:

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

Kapat, ancak içe aktarma işlevi aslında öğeleri almaz. Bu nedenle, urllib import * 'dan yararlanan bir kullanıcı, ihtiyaç duydukları fonksiyonları alamayacaktır. –

13

HTTPretty, FakeWeb'in yaptığı gibi çalışır. HTTPretty yuva katmanında çalışır, bu yüzden herhangi bir python http istemci kütüphanesini engellemeye çalışmalıdır. Bu, urllib2, httplib2'ye karşı test edildi ve

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

2013'te, bu kesinlikle en iyi cevaptır. Falcão'nun harika kütüphanesine oy verelim millet! – fatuhoku

+0

Obj-C açısından gelen, Python için [OHHTTPStubs] (https://github.com/AliSoftware/OHHTTPStubs) gibi bir şey arıyordum. HTTPretty'yi bulmaktan çok memnunum. – fatuhoku

İlgili konular