नकली के साथ आसानी से संपत्ति का मजाक कैसे उड़ाया जाए?


92

आप नकली के साथ आसानी से संपत्ति का कैसे मजाक उड़ाते हैं ?

मैंने कोशिश की:

setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())

लेकिन मुद्दा यह है कि यह तब वर्ग के सभी उदाहरणों पर लागू होता है ... जो मेरे परीक्षणों को तोड़ते हैं।

क्या आपके पास कोई अन्य विचार है? मैं पूरी वस्तु का मजाक नहीं उड़ाना चाहता, केवल इस विशिष्ट संपत्ति का।

जवाबों:


166

मुझे लगता है कि बेहतर तरीका यह है कि संपत्ति का मजाक उड़ाया जाए PropertyMock, बजाय इसके कि वह __get__सीधे तरीके से नकल करे।

यह प्रलेखन में कहा गया है , इसके लिए खोजें unittest.mock.PropertyMock: एक मॉक का उपयोग एक संपत्ति के रूप में, या अन्य विवरणक के रूप में एक वर्ग में किया जा सकता है। PropertyMockप्रदान करता है __get__और __set__तरीकों ताकि आप एक वापसी मूल्य निर्दिष्ट कर सकते हैं जब यह लाया जाता है।

यहां कैसे:

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

def test(unittest.TestCase):
    with mock.patch('MyClass.last_transaction', new_callable=PropertyMock) as mock_last_transaction:
        mock_last_transaction.return_value = Transaction()
        myclass = MyClass()
        print myclass.last_transaction
        mock_last_transaction.assert_called_once_with()

मुझे क्लास की विधि का मजाक उड़ाना पड़ा @property। इस उत्तर ने मेरे लिए काम किया जब अन्य उत्तर (और कई अन्य प्रश्नों पर अन्य उत्तर) नहीं थे।
एलनसे

3
यह वह तरीका है, जो किया जाना चाहिए। काश "स्वीकृत" उत्तर
हिलाने

4
मुझे लगता है कि संदर्भ प्रबंधक कॉल में वापसी मूल्य थोड़ा साफ होना चाहिए: `` mock.patch के साथ `('MyClass.last_transaction', new_callable = PropertyMock, return_value = Transaction ()): ...` ``
wodow

वास्तव में, मैं सिर्फ इस एक को स्वीकार जवाब ले जाया गया।
charlax

1
mock.patch.object का उपयोग करना भी अच्छा है क्योंकि आपको कक्षा का नाम स्ट्रिंग के रूप में लिखने की ज़रूरत नहीं है (उदाहरण में वास्तव में कोई समस्या नहीं है) और यदि आपने पैकेज का नाम बदलने का फैसला किया है और नहीं तो इसका पता लगाना / ठीक करना आसान है एक परीक्षण अपडेट किया गया
केविन

41

वास्तव में, जवाब था (हमेशा की तरह) दस्तावेज़ीकरण में , यह सिर्फ इतना है कि मैं उदाहरण के बाद जब मैं अपने उदाहरण के बाद वर्ग के बजाय पैच को लागू कर रहा था।

यहां है कि इसे कैसे करना है:

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

परीक्षण सूट में:

def test():
    # Make sure you patch on MyClass, not on a MyClass instance, otherwise
    # you'll get an AttributeError, because mock is using settattr and
    # last_transaction is a readonly property so there's no setter.
    with mock.patch(MyClass, 'last_transaction') as mock_last_transaction:
        mock_last_transaction.__get__ = mock.Mock(return_value=Transaction())
        myclass = MyClass()
        print myclass.last_transaction

14
लोगों को दूसरे उदाहरण का उपयोग करना चाहिए। mock.PropertyMockयह करने का तरीका है!
vitiral

4
यह सही है, लेखन के समय PropertyMockअस्तित्व में नहीं था।
charlax

6

यदि जिस ऑब्जेक्ट की संपत्ति को आप ओवरराइड करना चाहते हैं, वह एक मॉक ऑब्जेक्ट है, तो आपको उपयोग करने की आवश्यकता नहीं है patch

इसके बजाय, एक बना सकते हैं PropertyMockऔर फिर नकली के प्रकार पर संपत्ति को ओवरराइड कर सकते हैं । उदाहरण के लिए, mock_rows.pagesसंपत्ति को वापस करने के लिए ओवरराइड करना (mock_page, mock_page,):

mock_page = mock.create_autospec(reader.ReadRowsPage)
# TODO: set up mock_page.
mock_pages = mock.PropertyMock(return_value=(mock_page, mock_page,))
type(mock_rows).pages = mock_pages

1
बाम, बस मैं जो चाहता था (एक संपत्ति के साथ स्वतःस्फूर्त वस्तु)। और एक सहकर्मी से कम नहीं aD♂️
मार्क मैकडॉनल्ड

6

शायद शैली की बात है लेकिन अगर आप परीक्षण में सज्जाकार पसंद करते हैं, तो @ jamescastlefield का उत्तर कुछ इस तरह बदला जा सकता है:

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

class Test(unittest.TestCase):
    @mock.patch('MyClass.last_transaction', new_callable=PropertyMock)
    def test(self, mock_last_transaction):
        mock_last_transaction.return_value = Transaction()
        myclass = MyClass()
        print myclass.last_transaction
        mock_last_transaction.assert_called_once_with()

6

मामले में आप प्रयोग कर रहे हैं pytestके साथ pytest-mock, आप अपने कोड, यानी, सरल और भी संदर्भ चरनी का उपयोग कर से बचने कर सकते withबयान इस प्रकार है:

def test_name(mocker): # mocker is a fixture included in pytest-mock
    mocked_property = mocker.patch(
        'MyClass.property_to_be_mocked',
        new_callable=mocker.PropertyMock,
        return_value='any desired value'
    )
    o = MyClass()

    print(o.property_to_be_mocked) # this will print: any desired value

    mocked_property.assert_called_once_with()

0

यदि आप परीक्षण नहीं करना चाहते हैं कि नकली संपत्ति तक पहुँचा गया था या नहीं, तो आप इसे उम्मीद के साथ पैच कर सकते हैं return_value

with mock.patch(MyClass, 'last_transaction', Transaction()):
    ...

0

यदि आपको @propertyमूल पर भरोसा करने के लिए अपने नकली की आवश्यकता है __get__, तो आप अपना रिवाज बना सकते हैंMockProperty

class PropertyMock(mock.Mock):

    def __get__(self, obj, obj_type=None):
        return self(obj, obj_type)

उपयोग:

class A:

  @property
  def f(self):
    return 123


original_get = A.f.__get__

def new_get(self, obj_type=None):
  return f'mocked result: {original_get(self, obj_type)}'


with mock.patch('__main__.A.f', new_callable=PropertyMock) as mock_foo:
  mock_foo.side_effect = new_get
  print(A().f)  # mocked result: 123
  print(mock_foo.call_count)  # 1
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.