2013-01-22 18 views
5

मैं उपयोगकर्ता परिभाषित वस्तुओं के अनुक्रम को संसाधित कर रहा हूं। यह निम्नलिखित के समान दिखता है:मैं उन कॉल्स पर कैसे जोर दे सकता हूं जो पायथन मॉक के साथ अनुक्रम तर्क स्वीकार करते हैं?

class Thing(object): 
    def __init__(self, x, y): 
     self.x = x 
     self.y = y 

विधि मैं वर्तमान में परीक्षण कर रहा हूँ निम्न के समान कार्यक्षमता है:

def my_function(things): 
    x_calc = calculate_something(t.x for t in things) 
    y_calc = calculate_something(t.y for t in things) 
    return x_calc/y_calc 

समस्या मैं calculate_something के लिए कॉल परीक्षण कर रहा है का सामना करना पड़ रहा है। मैं बहुत तरह का दावा है कि इन कॉल हुआ, कुछ करना चाहते हैं:

calculateSomethingMock.assert_any_call(the_sequence) 

मैं अनुक्रम calculate_something में पारित कर दिया के आदेश के बारे में परवाह नहीं है, लेकिन मुझे परवाह है कि तत्वों सभी उपस्थित हैं। मैं जेनरेटर फ़ंक्शन को set पर कॉल में लपेट सकता हूं, लेकिन मुझे नहीं लगता कि मेरा परीक्षण यह निर्धारित करना चाहिए कि calculate_something में किस प्रकार का अनुक्रम पारित किया गया है। मैं इसे किसी भी तरह का अनुक्रम पारित करने में सक्षम होना चाहिए। मैं वैकल्पिक रूप से एक विधि बना सकता हूं जो जेनरेटर सिंटैक्स का उपयोग करने के बजाय अनुक्रम उत्पन्न करता है और उस विधि को नकली करता है, लेकिन यह ओवरकिल जैसा लगता है।

मैं इस दावे को सबसे अच्छी तरह से कैसे व्यवस्थित कर सकता हूं, या यहां मेरी परेशानी का परीक्षण खराब संरचित कोड का संकेत है?

मैं पाउथन 2.7.3 का उपयोग मॉक 1.0.1 के साथ कर रहा हूं।

(जो कोई मजबूर महसूस करता है उस पर टिप्पणी करने के लिए, मुझे पता है मैं परीक्षण पिछले कर रहा हूँ और है कि इस सबसे बड़ी अभ्यास नहीं माना जाता है कर रहा हूँ।)

संपादित करें:

घड़ी this marvelous talk entitled "Why You Don't Get Mock Objects by Gregory Moeck" के बाद , मैंने पुनर्विचार किया है कि मुझे calculate_something विधि का मज़ाक उड़ाया जाना चाहिए।

उत्तर

2

मैंने उस कोड को छुआ नहीं है जिसे मैं मूल रूप से अच्छी तरह से काम कर रहा था, लेकिन मैं आम तौर पर परीक्षण के लिए अपने दृष्टिकोण पर पुनर्विचार कर रहा हूं।मैं जो करता हूं उसके बारे में और अधिक सावधान रहने की कोशिश कर रहा हूं और नकली नहीं हूं। मुझे हाल ही में एहसास हुआ कि मैं बेहोशी से अंगूठे के इस नियम का पालन करना शुरू कर रहा था: अगर यह मेरा परीक्षण छोटा और सरल बनाता है तो इसे मजाक करें और अगर यह परीक्षण अधिक जटिल हो जाता है तो इसे अकेला छोड़ दें। इस विधि के मामले में सरल इनपुट/आउटपुट परीक्षण पर्याप्त है। डेटाबेस या फ़ाइलों जैसी कोई बाहरी निर्भरता नहीं है। तो संक्षेप में, मुझे लगता है कि मेरे प्रश्न का उत्तर है, "मुझे calculate_something नकल नहीं करना चाहिए।" ऐसा करने से मेरा परीक्षण पढ़ने और बनाए रखने के लिए कठिन हो जाता है।

7

नकली दस्तावेज़ीकरण को देखते हुए, call_args_list है जो आप चाहते हैं कि करेंगे।

तो आप अपने परीक्षण पर calculate_something पर नकल करेंगे।

calculate_something = Mock(return_value=None) 

आप my_function के बाद समाप्त हो गया है आप तर्क कर रही द्वारा पारित की जाँच कर सकते हैं:

calculate_something.call_args_list 

जो (पारित कर दिया इसी तत्वों के साथ) यह सब करने के लिए किए गए कॉल की एक सूची वापस आ जाएगी।

संपादित:

(क्षमा करें यह मेरे इतने लंबे समय ले लिया है, मैं अपने मशीन पर Python3.3 स्थापित करने के लिए किया था)

mymodule.py

class Thing: 
    ... 
def calculate_something: 
    ... 

def my_function(things): 
    # Create the list outside in order to avoid a generator object 
    # from being passed to the Mock object. 

    xs = [t.x for t in things] 
    x_calc = calculate_something(xs) 

    ys = [t.y for t in things] 
    y_calc = calculate_something(ys) 
    return True 

test_file। पीई

import unittest 
from unittest.mock import patch, call 
import mymodule 



class TestFoo(unittest.TestCase): 

    # You can patch calculate_something here or 
    # do so inside the test body with 
    # mymodule.calcualte_something = Mock() 
    @patch('mymodule.calculate_something') 
    def test_mock(self, mock_calculate): 

     things = [mymodule.Thing(3, 4), mymodule.Thing(7, 8)] 

     mymodule.my_function(things) 

     # call_args_list returns [call([3, 7]), call([4, 8])] 
     callresult = mock_calculate.call_args_list 


     # Create our own call() objects to compare against 
     xargs = call([3, 7]) 
     yargs = call([4, 8]) 

     self.assertEqual(callresult, [xargs, yargs]) 

     # or 
     # we can extract the call() info 
     # http://www.voidspace.org.uk/python/mock/helpers.html#mock.call.call_list 
     xargs, _ = callresult[0] 
     yargs, _ = callresult[1] 

     xexpected = [3, 7] 
     yexpected = [4, 8] 

     self.assertEqual(xargs[0], xexpected) 
     self.assertEqual(yargs[0], yexpected) 

if __name__ == '__main__': 
    unittest.main() 
+0

मैं उस सूची में खुदाई से बचने की कोशिश करता हूं, लेकिन मुझे लगता है कि यह एकमात्र तरीका हो सकता है। क्या आप एक उदाहरण प्रदान कर सकते हैं? – jpmc26

+0

धन्यवाद। यह परीक्षण विधि को संरक्षित करने के तरीके पर निर्भर करता है, और यह सूची में गुजरने वाली विधि पर भी निर्भर करता है। मैं आदेश पर भरोसा परीक्षण पर बहुत उत्सुक नहीं हूँ। यदि मैं 'गणना' कुछ 'में पारित तर्क के लिए कुछ अन्य डेटा संरचना में परिवर्तन करने का निर्णय लेता हूं, तो क्या मैं परीक्षण विफल होना चाहता हूं? या इसके विपरीत, यदि मैं एक अलग डेटा प्रकार के लिए कॉल की जांच करने के लिए परीक्षण बदलता हूं, तो क्या मैं इसे फिर से पास करने के लिए विधि को संशोधित करना चाहता हूं? – jpmc26

+0

हां, लेकिन याद रखें यह एक उदाहरण है। आप किसी भी अन्य डेटा संरचना की गणना 'गणना' कर सकते हैं, बस उस प्रतिबिंबित करने के लिए परीक्षण अपडेट करें (अपेक्षित मानों को बदलकर)। आप परीक्षा उत्तीर्ण करना चाहते हैं। आपने पहले ही देखा है कि 'call_args_list' का उपयोग करके' गणना 'कुछ' 'पर दिए गए तर्क कैसे प्राप्त करें। उसके बाद, यह केवल आपके द्वारा अपेक्षित डेटा के साथ पारित डेटा की तुलना करने की बात है। –