urllib

2008-11-17 15 views
64

जैसे एक मैक/स्टब पाइथन मॉड्यूल कैसे कर सकता है मुझे urllib.urlopen (यह urllib.urlencode का भी उपयोग करता है) का उपयोग कर बाहरी सर्वर पर किसी पृष्ठ से क्वेरी करने की आवश्यकता होती है। सर्वर नीचे हो सकता है, पेज बदल सकता है; मैं एक परीक्षण के लिए इस पर भरोसा नहीं कर सकता।urllib

urllib.urlopen क्या नियंत्रित करता है इसे नियंत्रित करने का सबसे अच्छा तरीका क्या है?

उत्तर

88

एक और सरल दृष्टिकोण अपने परीक्षण ओवरराइड 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() के बजाय बुलाया जाएगा। पाइथन जैसी गतिशील भाषाएं परीक्षण के लिए विधियों और कक्षाओं को बाहर निकालना बहुत आसान बनाती हैं।

परीक्षणों के लिए निर्भरताओं को रोकने के बारे में अधिक जानकारी के लिए http://softwarecorner.wordpress.com/ पर मेरे ब्लॉग पोस्ट देखें।

+11

परीक्षण के लिए बंदरगाह एक आसान बात है। दरअसल, यह शायद कैनोलिक "अच्छा बंदरगाह" उदाहरण है। –

+0

http://visionandexecution.org नीचे प्रतीत होता है। क्या कोई और लिंक है, या यह अब चला गया है? –

+1

मैंने ब्लॉग में वास्तव में लंबे समय तक पोस्ट नहीं किया है, लेकिन मैंने इसे http://softwarecorner.wordpress.com/ –

8

शायद इसे संभालने का सबसे अच्छा तरीका कोड को विभाजित करना है, ताकि पेज सामग्री को संसाधित करने वाले तर्क को पृष्ठ प्राप्त करने वाले कोड से विभाजित किया जा सके।

फिर प्रसंस्करण तर्क में फ़ेचर कोड का एक उदाहरण पास करें, फिर आप इकाई परीक्षण के लिए इसे आसानी से एक नकली 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 
3

सबसे आसान तरीका है अपने समारोह को बदलने के लिए इतना है कि यह जरूरी urllib.urlopen का उपयोग नहीं करता है। आइए मान लें कि यह आपका मूल कार्य है:

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

एक तर्क जोड़ें जो यूआरएल खोलने के लिए उपयोग करने के लिए कार्य है। तो फिर तुम जो कुछ भी आप की जरूरत है ऐसा करने के लिए एक नकली समारोह प्रदान कर सकते हैं:

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

यकीन है कि ऐसा नहीं है कि मैं परीक्षण विन्यास विवरण के बारे में पता के तहत स्थिरता होने की तरह है ... बहरहाल, यह काम करता है अनुरोध करता है। –

+1

मुझे फ़ंक्शन को पैरामीटर करने में कुछ भी गलत नहीं दिख रहा है। यहां कोई ज्ञान नहीं है कि कैसे urlopen faked हो सकता है या क्यों, बस यह हो सकता है। –

27

क्या आपने 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 ...> 
67

मैं Mock's पैच डेकोरेटर का उपयोग कर रहा: यहाँ एक सरल इंटरैक्टिव सत्र समाधान की जरूरत को दर्शाता हुआ है

from mock import patch 

[...] 

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

बहुत खराब यह मॉड्यूल फ़ंक्शंस को पैच करते समय काम नहीं करता है:/(कम से कम 0.7.2) –

+2

100% सत्य नहीं है, अगर आप इसे काम करने से पहले फ़ंक्शन आयात करते हैं, अन्यथा पैचिंग चुपचाप विफल हो जाती है (कोई त्रुटि नहीं, बस कुछ भी नहीं मिलता है : /) –

+2

अच्छा बिंदु वहां; पैचिंग को त्रुटियों को फेंकना चाहिए जब यह चुपचाप विफल होने की बजाय प्रासंगिक मॉड्यूल को ढूंढने में विफल रहा है। – fatuhoku

7

मामले में आप भी मॉड्यूल लोड करने के लिए नहीं करना चाहती:

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

बंद करें, लेकिन आयात फ़ंक्शन वास्तव में सामान आयात नहीं करेगा। तो urllib आयात * से उपयोग करने वाले कॉलर को 2013 में –

13

HTTPretty ठीक उसी तरह काम करता है जैसे FakeWeb करता है। HTTPretty सॉकेट परत में काम करता है, इसलिए इसे किसी भी पायथन 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" 
+0

की आवश्यकता नहीं होगी, यह निश्चित रूप से सबसे अच्छा जवाब है। चलो फाल्को की भयानक लाइब्रेरी को वोट दें, दोस्तों! – fatuhoku

+0

ओबीजे-सी कोण से आ रहा है, मैं पाइथन के लिए [OHHTTPStubs] (https://github.com/AliSoftware/OHHTTPStubs) जैसे कुछ ढूंढ रहा था। मुझे HTTPretty खोजने में खुशी है। – fatuhoku

संबंधित मुद्दे