इनपुट तर्कों के आधार पर नकली अजगर कार्य


150

हम थोड़ी देर के लिए अजगर के लिए मॉक का उपयोग कर रहे हैं ।

अब, हमारे पास एक ऐसी स्थिति है जिसमें हम एक फ़ंक्शन का मखौल बनाना चाहते हैं

def foo(self, my_param):
    #do something here, assign something to my_result
    return my_result

आम तौर पर, इसका मजाक उड़ाने का तरीका होगा (यह मानते हुए कि किसी वस्तु का हिस्सा होना)

self.foo = MagicMock(return_value="mocked!")

यहां तक ​​कि, अगर मैं फू () को दो बार कहता हूं तो मैं उपयोग कर सकता हूं

self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])

अब, मैं एक ऐसी स्थिति का सामना कर रहा हूं जिसमें मैं एक निश्चित मान वापस करना चाहता हूं जब इनपुट पैरामीटर का एक विशेष मूल्य होता है। तो अगर हम कहते हैं कि "my_param" "कुछ" के बराबर है तो मैं "my_cool_mock" लौटना चाहता हूं

यह अजगर के लिए मॉकिटो पर उपलब्ध लगता है

when(dummy).foo("something").thenReturn("my_cool_mock")

मैं खोज रहा हूं कि बिना किसी सफलता के साथ मॉक को कैसे हासिल किया जाए?

कोई विचार?


2
हो सकता है कि यह उत्तर मदद करेगा - stackoverflow.com/a/7665754/234606
naiquevin

@naiquevin यह पूरी तरह से समस्या साथी को धन्यवाद देता है, धन्यवाद!
जुआन एंटोनियो गोमेज़ मोरियानो

मुझे नहीं पता था कि आप पायथन के साथ मॉक्टियो का उपयोग कर सकते हैं, इसके लिए +1!
बेन

यदि आपका प्रोजेक्ट पाइस्टेस्ट का उपयोग करता है, तो ऐसे उद्देश्य के लिए आप लाभ उठाना चाहते हैं monkeypatch। मंकीपैच "इस फ़ंक्शन को परीक्षण के लिए प्रतिस्थापित करें" के लिए अधिक है, जबकि मॉक वह है जो आप तब उपयोग करते हैं जब आप यह भी जांचना चाहते हैं mock_callsया इसके बारे में क्या कहा जाता है और इसी तरह। दोनों के लिए एक जगह है, और मैं अक्सर दिए गए परीक्षण फ़ाइल में अलग-अलग समय पर दोनों का उपयोग करता हूं।
बहावकारक

जवाबों:


187

यदि side_effectकोई फ़ंक्शन है, तो जो भी फ़ंक्शन देता है वह वही है जो मॉक रिटर्न को कॉल करता है। side_effectसमारोह नकली रूप में एक ही तर्क के साथ कहा जाता है। यह आपको इनपुट के आधार पर कॉल के रिटर्न मान को गतिशील रूप से भिन्न कर सकता है:

>>> def side_effect(value):
...     return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]

http://www.voidspace.org.uk/python/mock/mock.html#calling


25
बस उत्तर को आसान बनाने के लिए, क्या आप साइड_फैक्ट फ़ंक्शन का नाम बदलकर कुछ और कर सकते हैं? (मुझे पता है, मुझे पता है, यह बहुत आसान है, लेकिन इस तथ्य को पठनीयता में सुधार करता है कि फ़ंक्शन नाम और परम नाम अलग हैं :)
जुआन एंटोनियो गोमेज़ मोरियानो

6
@JuanAntonioGomezMoriano मैं कर सकता था, लेकिन इस मामले में मैं सीधे दस्तावेज उद्धृत कर रहा हूं, इसलिए मैं बोली को संपादित करने के लिए थोड़ा निराश हूं अगर यह विशेष रूप से टूटा हुआ नहीं है।
एम्बर

और सिर्फ इन सभी वर्षों बाद में पंडिताऊ होने के लिए है, लेकिन side effectसटीक शब्द है: en.wikipedia.org/wiki/Side_effect_(computer_science)
LSH

7
@ यदि वे नाम के बारे में शिकायत नहीं कर रहे हैं CallableMixin.side_effect, लेकिन उदाहरण में परिभाषित अलग फ़ंक्शन का एक ही नाम है।
ऑरेंजडॉग

48

जैसा कि कई बार बुलाया विधि के साथ पायथन मॉक ऑब्जेक्ट में संकेत दिया गया है

एक समाधान है कि मैं अपना स्वयं का पक्ष लिखूं

def my_side_effect(*args, **kwargs):
    if args[0] == 42:
        return "Called with 42"
    elif args[0] == 43:
        return "Called with 43"
    elif kwargs['foo'] == 7:
        return "Foo is seven"

mockobj.mockmethod.side_effect = my_side_effect

वह चालबाजी करता है


2
इसने मेरे लिए चयनित उत्तर की तुलना में स्पष्ट कर दिया है, इसलिए आपके अपने प्रश्न का उत्तर देने के लिए धन्यवाद :)
लुका बीज़र्रा

15

साइड इफ़ेक्ट में एक फंक्शन लगता है (जो कि एक लैम्ब्डा फंक्शन भी हो सकता है ), इसलिए साधारण मामलों के लिए जो आप उपयोग कर सकते हैं:

m = MagicMock(side_effect=(lambda x: x+1))

4

मैंने " इनपुट तर्कों के आधार पर एक फ़ंक्शन को कैसे मॉक किया जाए" की तलाश में यहां समाप्त किया है और मैंने आखिरकार एक सरल ऑक्स फंक्शन बनाते हुए इसे हल किया:

def mock_responses(responses, default_response=None):
  return lambda input: responses[input] if input in responses else default_response

अभी:

my_mock.foo.side_effect = mock_responses(
  {
    'x': 42, 
    'y': [1,2,3]
  })
my_mock.goo.side_effect = mock_responses(
  {
    'hello': 'world'
  }, 
  default_response='hi')
...

my_mock.foo('x') # => 42
my_mock.foo('y') # => [1,2,3]
my_mock.foo('unknown') # => None

my_mock.goo('hello') # => 'world'
my_mock.goo('ey') # => 'hi'

आशा है कि यह किसी की मदद करेगा!


2

तुम भी उपयोग कर सकते हैं partialसे functoolsआप एक समारोह है कि पैरामीटर लेता उपयोग करना चाहते हैं, लेकिन समारोह आप मजाक कर रहे हैं नहीं करता है। इस तरह:

def mock_year(year):
    return datetime.datetime(year, 11, 28, tzinfo=timezone.utc)
@patch('django.utils.timezone.now', side_effect=partial(mock_year, year=2020))

यह एक ऐसा कॉल करने योग्य लौटाएगा जो मापदंडों को स्वीकार नहीं करता है (जैसे कि Django के टाइमज़ोन .now ()), लेकिन मेरा mock_year फ़ंक्शन करता है।


इस सुरुचिपूर्ण समाधान के लिए धन्यवाद। मुझे यह जोड़ना पसंद है कि यदि आपके मूल फ़ंक्शन में अतिरिक्त पैरामीटर हैं, तो उन्हें आपको उत्पादन कोड में कीवर्ड के साथ बुलाया जाना चाहिए या यह दृष्टिकोण काम नहीं करेगा। आप त्रुटि मिलती है: got multiple values for argument
एरिक कालकोकेन

1

बस इसे करने का दूसरा तरीका दिखाने के लिए:

def mock_isdir(path):
    return path in ['/var/log', '/var/log/apache2', '/var/log/tomcat']

with mock.patch('os.path.isdir') as os_path_isdir:
    os_path_isdir.side_effect = mock_isdir

1

आप यह भी उपयोग कर सकते हैं @mock.patch.object:

मान लीजिए कि एक डेटाबेस से पढ़ने के लिए एक मॉड्यूल my_module.pyका उपयोग किया pandasजाता है और हम इस मॉड्यूल को मॉकिंग pd.read_sql_tableविधि (जो table_nameतर्क के रूप में लेते हैं) का परीक्षण करना चाहते हैं ।

आप क्या कर सकते हैं (अपने परीक्षण के अंदर) एक db_mockविधि है जो प्रदान किए गए तर्क के आधार पर विभिन्न वस्तुओं को वापस करती है:

def db_mock(**kwargs):
    if kwargs['table_name'] == 'table_1':
        # return some DataFrame
    elif kwargs['table_name'] == 'table_2':
        # return some other DataFrame

अपने परीक्षण कार्य में आप तब करते हैं:

import my_module as my_module_imported

@mock.patch.object(my_module_imported.pd, "read_sql_table", new_callable=lambda: db_mock)
def test_my_module(mock_read_sql_table):
    # You can now test any methods from `my_module`, e.g. `foo` and any call this 
    # method does to `read_sql_table` will be mocked by `db_mock`, e.g.
    ret = my_module_imported.foo(table_name='table_1')
    # `ret` is some DataFrame returned by `db_mock`

1

यदि आप "इनपुट पैरामीटर का एक विशेष मान होने पर एक निश्चित मान लौटना चाहते हैं" , तो शायद आपको मॉक की भी आवश्यकता नहीं है और dictइसके getविधि के साथ उपयोग कर सकते हैं :

foo = {'input1': 'value1', 'input2': 'value2'}.get

foo('input1')  # value1
foo('input2')  # value2

यह तब अच्छी तरह से काम करता है जब आपके नकली का आउटपुट इनपुट की मैपिंग हो। जब यह इनपुट का कार्य होता है तो मैं एम्बर के उत्तर के side_effectअनुसार उपयोग करने का सुझाव देता हूं ।

यदि आप दोनों की Mockक्षमताओं ( assert_called_once, call_countआदि) को संरक्षित करना चाहते हैं, तो आप दोनों के संयोजन का उपयोग कर सकते हैं :

self.mock.side_effect = {'input1': 'value1', 'input2': 'value2'}.get

यह बहुत चालाक है।
एमिलर

-6

मुझे पता है कि यह एक बहुत पुराना सवाल है, अजगर लाम्बा के उपयोग से सुधार के रूप में मदद कर सकता है

self.some_service.foo.side_effect = lambda *args:"Called with 42" \
            if args[0] == 42 \
            else "Called with 42" if args[0] == 43 \
            else "Called with 43" if args[0] == 43 \
            else "Called with 45" if args[0] == 45 \
            else "Called with 49" if args[0] == 49 else None
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.