मैं कथन के साथ उपयोग किए गए एक खुले का उपयोग कैसे कर सकता हूं (पायथन में मॉक ढांचे का उपयोग करके)?


188

मैं मोक्स के साथ निम्नलिखित कोड का परीक्षण कैसे कर सकता हूं (मॉक का उपयोग करके, माइकल फ़ॉर्ड्स की रूपरेखा द्वारा पैच डेकोरेटर और प्रहरी ):

def testme(filepath):
    with open(filepath, 'r') as f:
        return f.read()

@ डेरिल स्पिट्जर: क्या आप मेटा-क्वेश्चन को छोड़ सकते हैं ("मुझे जवाब पता है ...") यह भ्रामक है।
एस.लॉट

अतीत में जब मैंने इसे छोड़ दिया है, तो लोगों ने शिकायत की है कि मैं अपने प्रश्न का उत्तर दे रहा हूं। मैं अपने उत्तर की ओर बढ़ने की कोशिश करूँगा।
डेरिल स्पिट्जर

1
@ डेरिल: किसी के स्वयं के प्रश्न का उत्तर देने के बारे में शिकायतों से बचने का सबसे अच्छा तरीका है, जो आमतौर पर "कर्म संचय" की चिंताओं से उपजा है, वह है प्रश्न और / या "समुदाय विकि" के रूप में उत्तर देना।
जॉन मिलिकिन

3
यदि आपके स्वयं के प्रश्न का उत्तर देने को कर्मा वेश्या माना जाता है, तो मुझे उस बिंदु पर स्पष्ट किया जाना चाहिए जो मुझे लगता है।
ईबीग्रीन

जवाबों:


132

इसे करने का तरीका मॉक 0.7.0 में बदल गया है जो अंत में पायथन प्रोटोकॉल विधियों (जादू के तरीकों) का समर्थन करता है, विशेष रूप से मैजिकमॉक का उपयोग करते हुए:

http://www.voidspace.org.uk/python/mock/magicmock.html

प्रसंग प्रबंधक के रूप में खुले हुए मॉकिंग का एक उदाहरण (नकली दस्तावेज़ में उदाहरण पृष्ठ से):

>>> open_name = '%s.open' % __name__
>>> with patch(open_name, create=True) as mock_open:
...     mock_open.return_value = MagicMock(spec=file)
...
...     with open('/some/path', 'w') as f:
...         f.write('something')
...
<mock.Mock object at 0x...>
>>> file_handle = mock_open.return_value.__enter__.return_value
>>> file_handle.write.assert_called_with('something')

वाह! यह वर्तमान में voidspace.org.uk/python/mock/magicmock.html पर संदर्भ-प्रबंधक उदाहरण की तुलना में बहुत सरल लगता है, जो स्पष्ट रूप से सेट करता है __enter__और __exit__साथ ही साथ वस्तुओं को नकली करने के लिए है - क्या उत्तरार्द्ध दृष्टिकोण पुराना है, या अभी भी उपयोगी है?
ब्रैंडन रोड्स

5
"बाद वाला दृष्टिकोण" यह दिखा रहा है कि मैजिकमॉक का उपयोग किए बिना इसे कैसे करना है (यानी यह सिर्फ एक उदाहरण है कि कैसे मॉक जादू के तरीकों का समर्थन करता है)। यदि आप एक मैजिकमॉक (ऊपर के रूप में) का उपयोग करते हैं तो आपके लिए प्रवेश और निकास पूर्व- निर्धारित हैं।
फुजिमैन

5
आप अपने ब्लॉग पोस्ट की ओर इशारा कर सकते हैं, जहाँ आप अधिक विवरण में समझाते हैं कि क्यों / कैसे काम करता है
रोड्रिग

9
पायथन 3 में, 'फ़ाइल' को परिभाषित नहीं किया गया है (मैजिकमॉक कल्पना में प्रयुक्त), इसलिए मैं इसके बजाय io.IOBase का उपयोग कर रहा हूं।
जोनाथन हार्टले

1
नोट: Python3 में बिल्टिन fileचला गया है!
प्रबुमा

239

mock_openmockफ्रेमवर्क का हिस्सा है और उपयोग करने के लिए बहुत सरल है। patchसंदर्भ के रूप में उपयोग किए गए ऑब्जेक्ट को पैच को बदलने के लिए उपयोग किया जाता है: आप इसका उपयोग अपने परीक्षण को सरल बनाने के लिए कर सकते हैं।

अजगर 3.x

के builtinsबजाय का उपयोग करें __builtin__

from unittest.mock import patch, mock_open
with patch("builtins.open", mock_open(read_data="data")) as mock_file:
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

पायथन 2.7

mockका हिस्सा नहीं है unittestऔर आपको पैच करना चाहिए__builtin__

from mock import patch, mock_open
with patch("__builtin__.open", mock_open(read_data="data")) as mock_file:
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

डेकोरेटर का मामला

यदि आप patchडेकोरेटर के mock_open()रूप में परिणाम के new patchतर्क के रूप में उपयोग करेंगे तो तर्क थोड़ा अजीब हो सकता है।

इस मामले में new_callable patchतर्क का उपयोग करना बेहतर है और याद रखें कि प्रत्येक अतिरिक्त तर्क जो patchउपयोग नहीं new_callableकरता है, उसे patchप्रलेखन में वर्णित कार्य करने के लिए पारित किया जाएगा ।

पैच () मनमाना कीवर्ड तर्क लेता है। इन्हें निर्माण पर मॉक (या new_callable) में पारित किया जाएगा।

उदाहरण के लिए पायथन 3.x के लिए सजाया गया संस्करण है:

@patch("builtins.open", new_callable=mock_open, read_data="data")
def test_patch(mock_file):
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

याद रखें कि इस मामले patchमें आप परीक्षण समारोह के तर्क के रूप में नकली वस्तु जोड़ देंगे।


पूछने के लिए क्षमा करें, with patch("builtins.open", mock_open(read_data="data")) as mock_file:डेकोरेटर सिंटैक्स में परिवर्तित किया जा सकता है? मैंने कोशिश की है, लेकिन मुझे यकीन नहीं है कि मुझे @patch("builtins.open", ...) दूसरे तर्क के रूप में पारित करने की आवश्यकता है ।
imrek

1
@DrunkenMaster Updateted .. इसके लिए धन्यवाद। इस मामले में डेकोरेटर का उपयोग करना तुच्छ नहीं है।
मिशेल

ग्रेजी! मेरी समस्या में थोड़ा और अधिक जटिल था (मैं चैनल की थी return_valueकी mock_openएक और नकली वस्तु में और दूसरा नकली का ज़ोर return_value), लेकिन यह जोड़ कर काम mock_openके रूप में new_callable
imrek

1
@ArthurZopellaro sixलगातार mockमॉड्यूल के लिए मॉड्यूल पर एक नज़र रखना । लेकिन मुझे नहीं पता कि यह builtinsएक सामान्य मॉड्यूल में भी है या नहीं।
मिशेल

1
आपको पैच लगाने के लिए सही नाम कैसे मिलेगा? यानी एक मनमाने ढंग से कार्य के लिए yo को @patch ('buildins.open') से पहला तर्क कैसे मिलता है?
zenperttu

73

नकली के नवीनतम संस्करणों के साथ, आप वास्तव में उपयोगी mock_open सहायक का उपयोग कर सकते हैं :

mock_open (नकली = कोई नहीं, read_data = कोई नहीं)

खुले के उपयोग को बदलने के लिए एक नकली बनाने के लिए एक सहायक कार्य करता है। यह खुले नाम से सीधे काम करता है या संदर्भ प्रबंधक के रूप में उपयोग किया जाता है।

मॉक तर्क कॉन्फ़िगर करने के लिए नकली वस्तु है। यदि कोई नहीं (डिफ़ॉल्ट) तो आपके लिए एक मैजिकमॉक बनाया जाएगा, जिसमें एपीआई मानक फ़ाइल हैंडल पर उपलब्ध विधियों या विशेषताओं तक सीमित है।

read_data फ़ाइल हैंडल के रीड मेथड के लिए एक स्ट्रिंग है जिसे वापस करना है। यह डिफ़ॉल्ट रूप से एक रिक्त स्ट्रिंग है।

>>> from mock import mock_open, patch
>>> m = mock_open()
>>> with patch('{}.open'.format(__name__), m, create=True):
...    with open('foo', 'w') as h:
...        h.write('some stuff')

>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')

यदि आप एक से अधिक .writeकॉल करते हैं, तो आप कैसे जांच करेंगे ?
n611x007

1
@naxa एक तरीका है, प्रत्येक अपेक्षित पैरामीटर को पास करना handle.write.assert_any_call()handle.write.call_args_listयदि ऑर्डर महत्वपूर्ण है तो आप प्रत्येक कॉल प्राप्त करने के लिए भी उपयोग कर सकते हैं ।
रोब कटमोर

m.return_value.write.assert_called_once_with('some stuff')बेहतर है इमो। कॉल रजिस्टर करने से बचा जाता है।
बेनामी

2
Mock.call_args_listकिसी भी Mock.assert_xxxतरीके को कॉल करने की तुलना में मैन्युअल रूप से जोर देना सुरक्षित है । यदि आप मॉक की विशेषताओं में से किसी भी बाद में गलत वर्तनी करते हैं, तो वे हमेशा चुपचाप गुजरेंगे।
जोनाथन हार्टले

12

एक साधारण फ़ाइल के लिए mock_open का उपयोग करने के लिए read()( इस पृष्ठ पर पहले से दिए गए मूल mock_open स्निपेट को लिखने के लिए अधिक तैयार किया गया है):

my_text = "some text to return when read() is called on the file object"
mocked_open_function = mock.mock_open(read_data=my_text)

with mock.patch("__builtin__.open", mocked_open_function):
    with open("any_string") as f:
        print f.read()

मॉक_पेन के लिए डॉक्स के अनुसार ध्यान दें, यह विशेष रूप से है read(), इसलिए for line in fउदाहरण के लिए सामान्य पैटर्न के साथ काम नहीं करेगा ।

अजगर 2.6.6 / नकली 1.0.1 का उपयोग करता है


अच्छा लग रहा है, लेकिन मैं इसे for line in opened_file:कोड के प्रकार के साथ काम नहीं कर सकता । मैंने पुनरावृत्ति करने वाले स्ट्रिंगरियो के साथ प्रयोग करने की कोशिश की, जो लागू होते हैं __iter__और इसके बजाय इसका उपयोग करते हैं my_text, लेकिन कोई भाग्य नहीं।
इलिनोइस

@EvgeniiPuchkaryov यह विशेष रूप read()से आपके for line in opened_fileमामले में काम नहीं करेगा के लिए काम करता है; मैंने पोस्ट को स्पष्ट करने के लिए संपादित किया है
jlb83

1
@EvgeniiPuchkaryov for line in f:समर्थन को इसके बजाय एक स्ट्रिंग ऑब्जेक्ट के open()रूप में रिटर्न मान का मजाक उड़ाकर प्राप्त किया जा सकता है ।
इस्कर जरक सिप

1
स्पष्ट करने के लिए, इस उदाहरण में परीक्षण (SUT) के तहत प्रणाली है: with open("any_string") as f: print f.read()
ब्रैड एम

4

शीर्ष उत्तर उपयोगी है, लेकिन मैंने इस पर थोड़ा विस्तार किया।

यदि आप अपने फ़ाइल ऑब्जेक्ट का मान सेट करना चाहते हैं, तो इसके लिए यहां दिए गए तर्कों के आधार पर ( fइन as f) open()करें:

def save_arg_return_data(*args, **kwargs):
    mm = MagicMock(spec=file)
    mm.__enter__.return_value = do_something_with_data(*args, **kwargs)
    return mm
m = MagicMock()
m.side_effect = save_arg_return_array_of_data

# if your open() call is in the file mymodule.animals 
# use mymodule.animals as name_of_called_file
open_name = '%s.open' % name_of_called_file

with patch(open_name, m, create=True):
    #do testing here

मूल रूप से, open()किसी ऑब्जेक्ट को वापस करेगा और उस ऑब्जेक्ट पर withकॉल करेगा __enter__()

ठीक से मॉक करने के लिए, हमें open()मॉक ऑब्जेक्ट को वापस करने के लिए मॉक करना होगा । उस नकली वस्तु को उसके बाद उस __enter__()कॉल को मॉक करना चाहिए ( MagicMockजो हमारे लिए यह करेगा) उस नकली डेटा / फ़ाइल ऑब्जेक्ट को वापस करना होगा जिसे हम चाहते हैं (इसलिए mm.__enter__.return_value)। ऐसा करने से 2 मोक्स ऊपर के रास्ते से open()गुजरते हैं, जिससे हम पास किए गए तर्कों को कैप्चर कर सकते हैं और उन्हें हमारे do_something_with_dataतरीके से पास कर सकते हैं ।

मैंने एक स्ट्रिंग के रूप में एक पूरी मॉक फाइल पास की open()और मुझे do_something_with_dataइस तरह देखा:

def do_something_with_data(*args, **kwargs):
    return args[0].split("\n")

यह स्ट्रिंग को एक सूची में बदल देता है ताकि आप एक सामान्य फ़ाइल के साथ निम्न कार्य कर सकें:

for line in file:
    #do action

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

क्या छूने से बचने का कोई तरीका है __enter__? यह निश्चित रूप से एक अनुशंसित तरीके से हैक की तरह दिखता है।
ट्रैक करें

दर्ज है कि कैसे खुले () की तरह शंकुधारी प्रबंधक हैं। नकली अक्सर थोड़े हैक होते हैं जिसमें आपको "निजी" सामान का उपयोग करने की आवश्यकता होती है, लेकिन यहाँ दर्ज नहीं किया जाता है आसानी से hacky imo
theannouncer

3

मुझे खेल में थोड़ी देर हो सकती है, लेकिन यह मेरे लिए तब काम करता है जब openएक नई फ़ाइल बनाने के बिना दूसरे मॉड्यूल में कॉल किया जाता है।

test.py

import unittest
from mock import Mock, patch, mock_open
from MyObj import MyObj

class TestObj(unittest.TestCase):
    open_ = mock_open()
    with patch.object(__builtin__, "open", open_):
        ref = MyObj()
        ref.save("myfile.txt")
    assert open_.call_args_list == [call("myfile.txt", "wb")]

MyObj.py

class MyObj(object):
    def save(self, filename):
        with open(filename, "wb") as f:
            f.write("sample text")

मेरे openअंदर __builtin__मॉड्यूल के अंदर फ़ंक्शन को पैच करके mock_open(), मैं बिना किसी को बनाए किसी फाइल को लिखने का मजाक उड़ा सकता हूं।

नोट: यदि आप एक ऐसे मॉड्यूल का उपयोग कर रहे हैं जो साइथॉन का उपयोग करता है, या आपका प्रोग्राम किसी भी तरह से साइथन पर निर्भर करता है, तो आपको अपनी फ़ाइल के शीर्ष पर शामिल करके साइथन के __builtin__मॉड्यूल को आयात करना होगा import __builtin____builtin__यदि आप साइथन का उपयोग कर रहे हैं तो आप सार्वभौमिक का मजाक नहीं उड़ा पाएंगे।


इस दृष्टिकोण का एक रूपांतर मेरे लिए काम आया, क्योंकि परीक्षण के तहत अधिकांश कोड अन्य मॉड्यूल में थे जैसा कि यहां दिखाया गया है। मुझे import __builtin__अपने परीक्षण मॉड्यूल में जोड़ना सुनिश्चित करने की आवश्यकता थी । इस लेख ने यह स्पष्ट करने में मदद की कि यह तकनीक क्यों काम करती है: ichimonji10.name/blog/6
किलथ्रश

0

निर्मित करने के लिए खुला () समारोह के साथ unittest पैच:

यह एक पैच के लिए एक जॉन्सन कॉन्फिगर पढ़ने के लिए काम करता है।

class ObjectUnderTest:
    def __init__(self, filename: str):
        with open(filename, 'r') as f:
            dict_content = json.load(f)

मॉक की गई वस्तु io.TextIOWrapper ऑब्जेक्ट ओपन () फ़ंक्शन द्वारा लौटाई गई है

@patch("<src.where.object.is.used>.open",
        return_value=io.TextIOWrapper(io.BufferedReader(io.BytesIO(b'{"test_key": "test_value"}'))))
    def test_object_function_under_test(self, mocker):

0

यदि आपको आगे किसी फ़ाइल की आवश्यकता नहीं है, तो आप परीक्षण विधि को सजा सकते हैं:

@patch('builtins.open', mock_open(read_data="data"))
def test_testme():
    result = testeme()
    assert result == "data"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.