2012-06-21 22 views
15
class HelloWorld(object): 
    def say_it(self): 
     return 'Hello I am Hello World' 

def i_call_hello_world(hw_obj): 
    print 'here... check type: %s' %type(HelloWorld) 
    if isinstance(hw_obj, HelloWorld): 
     print hw_obj.say_it() 

from mock import patch, MagicMock 
import unittest 

class TestInstance(unittest.TestCase): 
    @patch('__main__.HelloWorld', spec=HelloWorld) 
    def test_mock(self,MK): 
     print type(MK) 
     MK.say_it.return_value = 'I am fake' 
     v = i_call_hello_world(MK) 
     print v 

if __name__ == '__main__': 
    c = HelloWorld() 
    i_call_hello_world(c) 
    print isinstance(c, HelloWorld) 
    unittest.main() 

मजाक ट्रैस बैकisinstance और यहाँ

here... check type: <type 'type'> 
Hello I am Hello World 
True 
<class 'mock.MagicMock'> 
here... check type: <class 'mock.MagicMock'> 
E 
====================================================================== 
ERROR: test_mock (__main__.TestInstance) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "/usr/local/lib/python2.7/dist-packages/mock.py", line 1224, in patched 
    return func(*args, **keywargs) 
    File "t.py", line 18, in test_mock 
    v = i_call_hello_world(MK) 
    File "t.py", line 7, in i_call_hello_world 
    if isinstance(hw_obj, HelloWorld): 
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types 

---------------------------------------------------------------------- 
Ran 1 test in 0.002s 

Q1 है। यह त्रुटि क्यों फेंक दी गई है? वे <class type='MagicMock>

Q2 हैं। मैं मॉकिंग को कैसे रोकूं ताकि त्रुटि ठीक हो जाने पर पहली पंक्ति गुजर जाएगी?

doc

से आम तौर पर वर्ग एक वस्तु की विशेषता अपने प्रकार वापस आ जाएगी। एक spec वर्ग के साथ एक नकली वस्तु के लिए स्पेक वर्ग बदले में देता है।/के रूप में मुखौटा धारण कर लिया यह नकली वस्तुओं वस्तु वे स्थान ले रही हैं के लिए isinstance परीक्षण पास करने की अनुमति देता है:

mock = Mock(spec=3) 
isinstance(mock, int) 
True 

धन्यवाद

+2

अब आप जानते हैं कि 'isinstance' का उपयोग क्यों निराश है। –

+1

@MarkRansom हाँ यह बुरा है। लेकिन यह सुनिश्चित करने के लिए सबसे अच्छा अभ्यास क्या है कि हम जिस इंटरफ़ेस में पास करते हैं वह सही है? 'हैट्टर' या तो अंतर को हल करने के लिए प्रतीत नहीं होता है। दो ऑब्जेक्ट्स में एक ही विधि के नाम हो सकते हैं और गलत ऑब्जेक्ट का उपयोग टेस्ट पास करेगा, मुझे लगता है? मुझे लगता है कि सवाल का ध्यान बदल गया है! आह। – CppLearner

+0

यह बात है - पायथन के बारे में कई अच्छी चीजों में से एक यह है कि यह "बतख टाइपिंग" की अनुमति देता है जहां आप किसी ऑब्जेक्ट के सटीक प्रकार की परवाह नहीं करते हैं जब तक कि आप जो चाहते हैं वह करता है। आपको यह सुनिश्चित करने के लिए विधि नामकरण में कुछ सावधानी बरतनी पड़ सकती है कि आप एक ही नाम को दो अलग-अलग अर्थों से परिभाषित नहीं करते हैं, लेकिन यह अंत में कोड को बहुत लचीलापन लाता है। यह एक सुविधा है, एक बग नहीं। –

उत्तर

-4

isinstance का उपयोग न करें, इसके बजाय say_it विधि के होने की जाँच। यदि विधि मौजूद है, तो इसे कॉल करें:

if hasattr(hw_obj, 'say_it'): 
    print hw_obj.say_it() 

यह एक बेहतर डिजाइन है वैसे भी: प्रकार की जानकारी पर भरोसा करना अधिक भंगुर है।

+0

धन्यवाद। लेकिन पहला सवाल वास्तव में क्यों होगा कि यह उस त्रुटि को फेंक रहा है? और दो, इस परिवर्तन को अपनाना, यदि दो ओब्जे के पास एक ही विधि का नाम है, तो परीक्षा उत्तीर्ण होगी, है ना? – CppLearner

+1

मुझे नहीं पता कि त्रुटि क्यों फेंक दी गई है। इससे कोई फर्क नहीं पड़ता, जल्द ही आप 'isinstance' का उपयोग नहीं करेंगे :) और हाँ, अब आप किसी ऑब्जेक्ट में पास कर सकते हैं जिसमें विधि है, और" ठीक से "व्यवहार करता है, और परीक्षण पास हो जाएगा। –

+0

धन्यवाद। प्रमुख फॉलबैक पढ़ने के बाद मैं इंस्टेंसेंस का उपयोग बंद कर दूंगा। अभी भी ... यदि 'MyMomKitchenObject' में' say_it' है और प्रोग्रामर फ़ंक्शन के इनपुट के रूप में उपयोग करता है ... परीक्षण अभी भी पास होगा, है ना? तो मैं अपने अजीब वास्तव में काम कैसे सत्यापित करूँ? या मुझे अपने कोड में इसकी शुद्धता कैसे निर्धारित करनी चाहिए? एकीकरण परीक्षण की तरह, दो ओबीजे में 99% समान इंटरफ़ेस हो सकता है, और परीक्षण के तहत सिस्टम कभी भी 1% अलग नहीं होता है, और परीक्षण अभी भी गुजरता है, सिस्टम "बिना किसी समस्या के काम करेगा"। – CppLearner

23

आईएमएचओ यह एक अच्छा सवाल है और कह रहा है "isinstance का उपयोग न करें, के बजाय बतख टाइपिंग का उपयोग करें" एक बुरा जवाब है। बतख टाइपिंग बहुत अच्छी है, लेकिन चांदी की गोली नहीं है। कभी-कभी isinstance आवश्यक है, भले ही यह पाइथोनिक न हो। उदाहरण के लिए, यदि आप कुछ पुस्तकालय या विरासत कोड के साथ काम करते हैं जो कि पाइथोनिक नहीं है तो आपको isinstance के साथ खेलना होगा। यह सिर्फ असली दुनिया है और नकली इस तरह के काम को फिट करने के लिए डिज़ाइन किया गया था।

कोड बड़ी गलती है में आप जब लिखें:

@patch('__main__.HelloWorld', spec=HelloWorld) 
def test_mock(self,MK): 

patch documentation हम पढ़ से (पर जोर मेरा है):

, लक्ष्य समारोह की या बयान के साथ शरीर के अंदर एक नया ऑब्जेक्ट के साथ पैच किया गया है।

इसका मतलब है कि जब आप HelloWorldवर्ग वस्तुHelloWorld के संदर्भ test_mock() समारोह के संदर्भ के लिए एक MagicMock वस्तु से बदल दिया जाएगा पैच।

तब, जब i_call_hello_world()if isinstance(hw_obj, HelloWorld): में निष्पादित किया जाता है HelloWorld एक MagicMock() वस्तु और नहीं एक वर्ग (त्रुटि पता चलता है के रूप में) है।

यह व्यवहार इसलिए है क्योंकि कक्षा संदर्भ को पैच करने के दुष्प्रभाव के रूप में isinstance(hw_obj, HelloWorld) का दूसरा तर्क एक वस्तु बन जाता है (MagicMock उदाहरण)। यह न तो class या type है।

HelloWorld_cache = HelloWorld 

def i_call_hello_world(hw_obj): 
    print 'here... check type: %s' %type(HelloWorld_cache) 
    if isinstance(hw_obj, HelloWorld_cache): 
     print hw_obj.say_it() 

त्रुटि गायब हो जाएगा क्योंकि HelloWorld वर्ग के लिए मूल संदर्भ HelloWorld_cache में सहेजा गया है जब आप मॉड्यूल लोड: इस व्यवहार को समझने के लिए एक सरल प्रयोग इस प्रकार i_call_hello_world() संशोधित करने के लिए है। जब पैच लागू होता है तो यह केवल HelloWorld और HelloWorld_cache नहीं बदलेगा।

दुर्भाग्यवश, पिछला प्रयोग हमें आपके जैसे मामलों के साथ खेलने का कोई तरीका नहीं देता है क्योंकि आप इस तरह की चाल शुरू करने के लिए लाइब्रेरी या विरासत कोड को नहीं बदल सकते हैं। इसके अलावा, ये ऐसी ही चाल हैं जिन्हें हम अपने कोड में कभी नहीं देखना चाहते हैं।

अच्छी खबर यह है कि आप कुछ कर सकते हैं कि है, लेकिन आप कर सकते हैं न सिर्फ patch मॉड्यूल जहां परीक्षण करने के लिए isinstace(o,HelloWord) कोड है में HelloWord संदर्भ। सबसे अच्छा तरीका वास्तविक मामले पर निर्भर करता है जिसे आपको हल करना होगा। आपके उदाहरण में आप HelloWorld ऑब्जेक्ट के रूप में उपयोग करने के लिए बस Mock बना सकते हैं, spec का उपयोग इसे HelloWorld उदाहरण के रूप में तैयार करने के लिए करें और isinstance परीक्षण पास करें। यह उन उद्देश्यों में से एक है जिसके लिए spec डिज़ाइन किया गया है। आपका परीक्षण इस तरह लिखा जाएगा:

def test_mock(self): 
    MK = MagicMock(spec=HelloWorld) #The hw_obj passed to i_call_hello_world 
    print type(MK) 
    MK.say_it.return_value = 'I am fake' 
    v = i_call_hello_world(MK) 
    print v 

और बस unittest भाग के उत्पादन में है

<class 'mock.MagicMock'> 
here... check type: <type 'type'> 
I am fake 
None 
+0

यह कथन गलत है: "हैलोवर्ल्ड एक मॉक() ऑब्जेक्ट है, न कि क्लास (जैसा कि त्रुटि सुझाई गई है)।" यदि आप मूल टाइपरर और डीबग को पकड़ते हैं, तो आप देखेंगे कि 'टाइप (हैलोवर्ल्ड)' निष्पादित करने से ' भी वापस आ जाएगा। –

+0

@seanazlin मैं इसे बाद में देखूंगा। लेकिन त्रुटि क्यों कहती है कि हैलोवार्ड कक्षा या प्रकार नहीं है? वैसे भी आपकी प्रतिक्रिया के लिए धन्यवाद। –

+0

@ सेनएज़लिन कृपया अपने पायथन कंसोल का पालन करें कथन का पालन करें: 'टाइप (मैजिकमॉक())', 'टाइप (मैजिकमॉक)', 'टाइप (ऑब्जेक्ट())', 'टाइप (ऑब्जेक्ट)'। उसके बाद मुझे उम्मीद है कि आप समझेंगे कि मैंने जो लिखा था वह सही है। वैसे भी अगर मैं आपकी टिप्पणी के बारे में 'मैजिकमॉक' के बजाय 'मॉक' लिखता था, तो मुझे लगता है कि यह गलत है लेकिन बहुत बड़ा मुद्दा नहीं है ... यह सिर्फ एक विस्तार है जिसे मैं ठीक कर दूंगा। जब आप डाउनवोट का उपयोग करते हैं तो अधिक ध्यान दें। मेरा जवाब सही है और यह केवल एकमात्र ऐसा है जो बिना किसी सवाल के मूल प्रश्न को कवर करता है * अरे लड़का! ऐसा मत करो " –

0

मैं इस के साथ अपने आप को हाल ही में कुश्ती किया गया है, जबकि कुछ इकाई परीक्षण लेखन। एक संभावित समाधान वास्तव में पूरे हैलोवर्ल्ड क्लास को नकल करने की कोशिश नहीं करना है, बल्कि उस क्लास के तरीकों का नकल करना है जिसे आप परीक्षण कर रहे कोड द्वारा बुलाए जाते हैं। उदाहरण के लिए, कुछ इस तरह काम करना चाहिए:

class HelloWorld(object): 
    def say_it(self): 
     return 'Hello I am Hello World' 

def i_call_hello_world(hw_obj): 
    if isinstance(hw_obj, HelloWorld): 
     return hw_obj.say_it() 

from mock import patch, MagicMock 
import unittest 

class TestInstance(unittest.TestCase): 
    @patch.object(HelloWorld, 'say_it') 
    def test_mock(self, mocked_say_it): 
     mocked_say_it.return_value = 'I am fake' 
     v = i_call_hello_world(HelloWorld()) 
     self.assertEquals(v, 'I am fake') 
+0

यदि आपको ** ** कक्षा की नकल की आवश्यकता है तो क्या है क्योंकि परीक्षण के संदर्भ में आंतरिक तरीकों का उपयोग नहीं किया जा सकता है? * इंस्टेंस और मॉकिंग * के बारे में बात करने वाला प्रश्न * नहीं * एक विधि का मज़ाक उड़ा रहा है * और क्यू 1 और क्यू 2 के जवाब कहां हैं? मैं आपके उत्तर को कम नहीं करता जैसा आपने किया था लेकिन शायद आपको फ़ाइल उत्तरों से पहले प्रश्नों पर अधिक ध्यान देना होगा। तकनीकी दृष्टि से आपके उत्तर के बारे में क्यों 'patch.object' का उपयोग करें, आपको वास्तव में इसकी आवश्यकता नहीं है। आप '@ पैच ('__ मुख्य __। HelloWorld.say_it', return_value = 'मैं नकली हूं') का उपयोग कर सकता हूं, जो पढ़ने के लिए अधिक संक्षिप्त और सरल है। –

+0

@ मिशेल अमीको फाइन पॉइंट्स। जो कुछ मैं प्रस्तावित कर रहा हूं वह कुछ स्थितियों के लिए केवल एक संभावित समाधान है (या हो सकता है कि कार्य-आस-पास एक बेहतर शब्द है), जैसे कि जब कोई यूनिट एक फ़ंक्शन का परीक्षण करता है जो isinstance() का उपयोग करता है और वह उस वर्ग की विधि को कॉल करता है जो ' इस मुद्दे के कारण मजाक नहीं किया जाएगा। मुझे लगता है कि पहले से सुझाए गए समाधानों के मुकाबले काम-आसपास कुछ अधिक आकर्षक हो सकता है। यह मेरी स्थिति में मेरे लिए काम किया। –

+0

आपके पास 'isinstance() 'कॉल होने पर आप क्या नहीं कर सकते हैं, बस कक्षा को पैच करें, लेकिन आप ऑब्जेक्ट के लिए एक मॉक का उपयोग कर सकते हैं। बीटीडब्ल्यू: जब आप 'patch.object' का उपयोग करते हैं तो ध्यान दें: इसे केवल तभी उपयोग करें जब आपको वास्तव में इसकी आवश्यकता हो या बहुत समय आपको समझ में नहीं आता कि आपका पैच क्यों काम नहीं करता है। (मेरे उत्तर से डाउनवोट को हटाने के लिए विचार करें क्योंकि आपका अवलोकन गलत है) –

2

मिशेल डी 'एमिको मेरे विचार में correct answer प्रदान करता है और मैं दृढ़ता से इसे पढ़ने की सलाह देते हैं। लेकिन मुझे थोड़ी देर लग गई और मुझे यकीन है कि मैं भविष्य में इस प्रश्न पर वापस आऊंगा, मैंने सोचा कि एक न्यूनतम कोड उदाहरण समाधान को स्पष्ट करने में मदद करेगा और त्वरित संदर्भ प्रदान करेगा:

from mock import patch, mock 

class Foo(object): pass 

# Cache the Foo class so it will be available for isinstance assert. 
FooCache = Foo 

with patch('__main__.Foo', spec=Foo): 
    foo = Foo() 
    assert isinstance(foo, FooCache) 
    assert isinstance(foo, mock.mock.NonCallableMagicMock) 

    # This will cause error from question: 
    # TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types 
    assert isinstance(foo, Foo) 
संबंधित मुद्दे