2010-09-30 9 views
46

मान लीजिए मैं एक अजगर इकाई परीक्षण में निम्न कोड:का दावा है कि एक विधि एक अजगर इकाई परीक्षण में बुलाया गया था

aw = aps.Request("nv1") 
aw2 = aps.Request("nv2", aw) 

वहाँ का दावा करने के लिए एक विशेष विधि है कि (मेरे मामले aw.Clear() में) एक आसान तरीका है परीक्षण की दूसरी पंक्ति के दौरान बुलाया गया था? जैसे

#pseudocode: 
assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw)) 

उत्तर

80

मैं इसके लिए Mock का उपयोग करें:

from mock import patch 
from PyQt4 import Qt 

@patch.object(Qt.QMessageBox, 'aboutQt') 
def testShowAboutQt(self, mock): 
    self.win.actionAboutQt.trigger() 
    self.assertTrue(mock.called) 

अपने मामले के लिए, यह ऐसा दिखाई दे सकता:

import mock 

def testClearWasCalled(self): 
    aw = aps.Request("nv1") 
    with patch.object(aw, 'Clear') as mock: 
     aw2 = aps.Request("nv2", aw) 

    mock.assert_called_with(42) # or mock.assert_called_once_with(42) 

मॉक एक वस्तु या मॉड्यूल पैच करने के लिए तरीके भी शामिल हैं, काफी कुछ उपयोगी सुविधाओं का समर्थन करता है , साथ ही साथ यह जांच कर रहा था कि सही चीज़ कहलाती थी, इत्यादि।

Caveat emptor! (क्रेता सावधान!)

आप (assert_called_once या assert_called_wiht करने के लिए) assert_called_with गलत टाइप यदि आपकी परीक्षण अभी भी चल सकते हैं नकली सोचेंगे यह एक मज़ाक उड़ाया समारोह है और खुशी के साथ जाना है, जब तक आप autospec=true का उपयोग करें। अधिक जानकारी के लिए assert_called_once: Threat or Menace पढ़ें।

+3

+1 अद्भुत मॉक मॉड्यूल के साथ मेरी दुनिया को विघटित करने के लिए +1। –

+0

@RonCohen: हाँ, यह बहुत अद्भुत है, और हर समय भी बेहतर हो रहा है। :) – Macke

+1

मॉक का उपयोग करते समय निश्चित रूप से जाने का तरीका है, मैं assert_called_once का उपयोग करने के खिलाफ सलाह दूंगा, बस अस्तित्व में नहीं है :) – FelixCQ

4

आप बाहर नकली कर सकते हैं aw.Clear, या तो स्वयं या pymox की तरह एक परीक्षण ढांचे का उपयोग कर: वहाँ कुछ इस तरह है। मैन्युअल रूप से, आप इसे कुछ इस तरह का उपयोग कर चाहते हैं:

class MyTest(TestCase): 
    def testClear(): 
    old_clear = aw.Clear 
    clear_calls = 0 
    aw.Clear = lambda: clear_calls += 1 
    aps.Request('nv2', aw) 
    assert clear_calls == 1 
    aw.Clear = old_clear 

pymox का उपयोग करना, आप इसे इस तरह करना चाहते हैं:

class MyTest(mox.MoxTestBase): 
    def testClear(): 
    aw = self.m.CreateMock(aps.Request) 
    aw.Clear() 
    self.mox.ReplayAll() 
    aps.Request('nv2', aw) 
+0

:

यहाँ अपने मामले में एक त्वरित उदाहरण है। इससे यह स्पष्ट हो जाता है कि क्या हो रहा है। –

7

हाँ, मैं आप रूपरेखा दे सकते हैं, लेकिन मेरे अजगर एक है थोड़ा जंगली और मैं विस्तार से व्याख्या करने में बहुत व्यस्त हूं।

मूल रूप से, आप विधि है कि मूल, जैसे फोन करेगा में एक प्रॉक्सी रखना होगा:

class fred(object): 
    def blog(self): 
    print "We Blog" 


class methCallLogger(object): 
    def __init__(self, meth): 
    self.meth = meth 

    def __call__(self, code=None): 
    self.meth() 
    # would also log the fact that it invoked the method 

#example 
f = fred() 
f.blog = methCallLogger(f.blog) 

के बारे में प्रतिदेय यह StackOverflow answer मदद मिल सकती है कि आप ऊपर समझते हैं।

और अधिक विस्तार में:

हालांकि इस सवाल का जवाब स्वीकार कर लिया गया, ग्लेन के साथ दिलचस्प चर्चा की वजह से और नि: शुल्क कुछ ही मिनटों के होने, मैं अपने जवाब पर विस्तार करने के लिए चाहता था:

# helper class defined elsewhere 
class methCallLogger(object): 
    def __init__(self, meth): 
    self.meth = meth 
    self.was_called = False 

    def __call__(self, code=None): 
    self.meth() 
    self.was_called = True 

#example 
class fred(object): 
    def blog(self): 
    print "We Blog" 

f = fred() 
g = fred() 
f.blog = methCallLogger(f.blog) 
g.blog = methCallLogger(g.blog) 
f.blog() 
assert(f.blog.was_called) 
assert(not g.blog.was_called) 
+0

अच्छा। मैंने methCallLogger को कॉल गिनती जोड़ दी है, इसलिए मैं इस पर जोर दे सकता हूं। –

+0

यह पूरी तरह से आत्मनिर्भर समाधान प्रदान किया गया है? गंभीरता से? –

+0

@Glenn मैं पाइथन के लिए बहुत नया हूं - शायद आपका एक बेहतर है - मैं अभी तक यह सब समझ नहीं पा रहा हूं। मैं इसे बाद में कोशिश करने में थोड़ा सा समय बिताऊंगा। –

13

मैं मुझे अंतर्निहित कुछ भी पता नहीं है। इसे लागू करने के लिए बहुत सरल है:

class assertMethodIsCalled(object): 
    def __init__(self, obj, method): 
     self.obj = obj 
     self.method = method 

    def called(self, *args, **kwargs): 
     self.method_called = True 
     self.orig_method(*args, **kwargs) 

    def __enter__(self): 
     self.orig_method = getattr(self.obj, self.method) 
     setattr(self.obj, self.method, self.called) 
     self.method_called = False 

    def __exit__(self, exc_type, exc_value, traceback): 
     assert getattr(self.obj, self.method) == self.called, 
      "method %s was modified during assertMethodIsCalled" % self.method 

     setattr(self.obj, self.method, self.orig_method) 

     # If an exception was thrown within the block, we've already failed. 
     if traceback is None: 
      assert self.method_called, 
       "method %s of %s was not called" % (self.method, self.obj) 

class test(object): 
    def a(self): 
     print "test" 
    def b(self): 
     self.a() 

obj = test() 
with assertMethodIsCalled(obj, "a"): 
    obj.b() 

यह जरूरी है कि वस्तु ही self.b, जो लगभग हमेशा सच है बदलाव नहीं करेगी।

+0

मैंने कहा कि मेरा पायथन जंगली था, हालांकि मैंने यह सुनिश्चित करने के लिए अपने समाधान का परीक्षण किया था कि यह काम करता है :-) मैंने संस्करण 2.5 से पहले पायथन को आंतरिक बनाया, वास्तव में मैंने किसी भी महत्वपूर्ण पायथन के लिए 2.5 का उपयोग नहीं किया क्योंकि हमें lib अनुकूलता के लिए 2.3 पर फ्रीज करना पड़ा था। आपके समाधान की समीक्षा में मैंने http://effbot.org/zone/python-with-statement.htm को एक अच्छा स्पष्ट विवरण के रूप में पाया। मैं विनम्रतापूर्वक सुझाव देता हूं कि मेरा दृष्टिकोण छोटा दिखता है और यदि आप नेस्टेड "के साथ" लॉगिंग के एक से अधिक बिंदु चाहते हैं तो आवेदन करना आसान हो सकता है। मैं वास्तव में आपको यह बताना चाहता हूं कि आपके कोई विशेष लाभ हैं या नहीं। –

+0

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

+0

+1, साफ विचार! –

10

हाँ यदि आप पाइथन 3.3+ का उपयोग कर रहे हैं। बुलाए गए विधि पर जोर देने के लिए आप बिल्ट-इन unittest.mock का उपयोग कर सकते हैं। पायथन 2.6+ के लिए रोलिंग बैकपोर्ट Mock का उपयोग करें, जो वही बात है।मैं इस दृष्टिकोण भी पसंद है, हालांकि मैं अभी भी old_clear कहा जाता प्राप्त करना चाहते हैं

from unittest.mock import MagicMock 
aw = aps.Request("nv1") 
aw.Clear = MagicMock() 
aw2 = aps.Request("nv2", aw) 
assert aw.Clear.called 
संबंधित मुद्दे

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