2015-04-26 9 views
16

निम्नलिखित कोड TypeError: 'Mock' object is not iterableImBeingTested.i_call_other_coroutines में विफल रहता है क्योंकि मैंने ImGoingToBeMocked को एक मॉक ऑब्जेक्ट से बदल दिया है।एसिन्सीओ कोरआउट्स का नकल कैसे करें?

मैं कोरउटिन का नकल कैसे कर सकता हूं?

class ImGoingToBeMocked: 
    @asyncio.coroutine 
    def yeah_im_not_going_to_run(self): 
     yield from asyncio.sleep(1) 
     return "sup" 

class ImBeingTested: 
    def __init__(self, hidude): 
     self.hidude = hidude 

    @asyncio.coroutine 
    def i_call_other_coroutines(self): 
     return (yield from self.hidude.yeah_im_not_going_to_run()) 

class TestImBeingTested(unittest.TestCase): 

    def test_i_call_other_coroutines(self): 
     mocked = Mock(ImGoingToBeMocked) 
     ibt = ImBeingTested(mocked) 

     ret = asyncio.get_event_loop().run_until_complete(ibt.i_call_other_coroutines()) 

उत्तर

11

के बाद से mock पुस्तकालय coroutines का समर्थन नहीं करता मेरी नक़ल coroutines मैन्युअल बना सकते हैं और नकली वस्तु के लिए उन आवंटित। थोड़ा और वर्बोज़ लेकिन यह काम करता है।

आपका उदाहरण कुछ ऐसा नज़र हो सकता है:

import asyncio 
import unittest 
from unittest.mock import Mock 


class ImGoingToBeMocked: 
    @asyncio.coroutine 
    def yeah_im_not_going_to_run(self): 
     yield from asyncio.sleep(1) 
     return "sup" 


class ImBeingTested: 
    def __init__(self, hidude): 
     self.hidude = hidude 

    @asyncio.coroutine 
    def i_call_other_coroutines(self): 
     return (yield from self.hidude.yeah_im_not_going_to_run()) 


class TestImBeingTested(unittest.TestCase): 

    def test_i_call_other_coroutines(self): 
     mocked = Mock(ImGoingToBeMocked) 
     ibt = ImBeingTested(mocked) 

     @asyncio.coroutine 
     def mock_coro(): 
      return "sup" 
     mocked.yeah_im_not_going_to_run = mock_coro 

     ret = asyncio.get_event_loop().run_until_complete(
      ibt.i_call_other_coroutines()) 
     self.assertEqual("sup", ret) 


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

खैर यह स्पष्ट है और अब मैं सवाल पूछने के लिए गूंगा है! धन्यवाद! –

+0

मैंने इसे इस जवाब में एक सहायक और सामान के साथ विस्तारित किया: http://stackoverflow.com/a/29905620/23972 –

12

की दूर Springing एंड्रयू Svetlov के answer, मैं सिर्फ यह सहायक समारोह साझा करना चाहते थे:

def get_mock_coro(return_value): 
    @asyncio.coroutine 
    def mock_coro(*args, **kwargs): 
     return return_value 

    return Mock(wraps=mock_coro) 

यह आपको मानक assert_called_with, call_count उपयोग करने देता है और अन्य तरीकों और एक नियमित unittest विशेषताएँ। मॉक आपको देता है।

आप की तरह सवाल में कोड के साथ इस का उपयोग कर सकते हैं:

class ImGoingToBeMocked: 
    @asyncio.coroutine 
    def yeah_im_not_going_to_run(self): 
     yield from asyncio.sleep(1) 
     return "sup" 

class ImBeingTested: 
    def __init__(self, hidude): 
     self.hidude = hidude 

    @asyncio.coroutine 
    def i_call_other_coroutines(self): 
     return (yield from self.hidude.yeah_im_not_going_to_run()) 

class TestImBeingTested(unittest.TestCase): 

    def test_i_call_other_coroutines(self): 
     mocked = Mock(ImGoingToBeMocked) 
     mocked.yeah_im_not_going_to_run = get_mock_coro() 
     ibt = ImBeingTested(mocked) 

     ret = asyncio.get_event_loop().run_until_complete(ibt.i_call_other_coroutines()) 
     self.assertEqual(mocked.yeah_im_not_going_to_run.call_count, 1) 
+0

'wraps' कीवर्ड के लिए +1, जिसने मुझे अपना उद्देश्य थोड़ा और समझ लिया। त्वरित अनुवर्ती प्रश्न; मैकड कोरआउट से एक से अधिक मूल्य लौटने के बारे में आप कैसे जाएंगे? यानी 'पढ़ा() 'एक कोरआउट है और आप पहले कुछ डेटा' b'data 'वापस करना चाहते हैं, और उसके बाद एक ईओएफ जैसी स्थिति लौटाएं (उदा। कोई डेटा,' बी 'या' कोई नहीं ')। AFAICS आप मॉक किए गए कोरआउट पर '.return_value' या '.side_effect' का उपयोग नहीं कर सकते हैं (' खराब उपज 'त्रुटि देता है)। – AlexandreH

2

डस्टिन का जवाब शायद अधिकांश मामलों में सही से एक है। मेरे पास एक अलग मुद्दा था जहां कोरआउट को एक से अधिक मूल्य लौटने की आवश्यकता थी, उदा। read() ऑपरेशन को अनुकरण करना, जैसा कि मेरे comment में संक्षेप में वर्णित है। तो

def test_some_read_operation(self): 
    #... 
    data = iter([b'data', b'']) 
    @asyncio.coroutine 
    def read(*args): 
     return next(data) 
    mocked.read = Mock(wraps=read) 
    # Here, the business class would use its .read() method which 
    # would first read 4 bytes of data, and then no data 
    # on its second read. 

, पर विस्तार:

कुछ और परीक्षण के बाद, नीचे दिए गए कोड मेरे लिए काम किया, मजाक समारोह के बाहर एक इटरेटर को परिभाषित करने, प्रभावी ढंग से अंतिम मान को याद करके अगले एक भेजने के लिए लौट आए डस्टिन का जवाब है, यह दिखाई देगा:

def get_mock_coro(return_values): 
    values = iter(return_values) 
    @asyncio.coroutine 
    def mock_coro(*args, **kwargs): 
     return next(values) 

    return Mock(wraps=mock_coro) 

दो तत्काल कमियां मैं इस दृष्टिकोण में देख सकते हैं कर रहे हैं:

  1. यह अपवादों को आसानी से बढ़ाने की अनुमति नहीं देता है (उदा। पहले कुछ डेटा वापस करें, फिर दूसरे पढ़ने के ऑपरेशन पर एक त्रुटि उठाएं)।
  2. मुझे मानक Mock.side_effect या .return_value विशेषताओं को अधिक स्पष्ट और पठनीय बनाने के लिए विशेषताओं का उपयोग करने का कोई तरीका नहीं मिला है।
9

मैं एक रैपर को एकजुट करने के लिए लिख रहा हूं जिसका उद्देश्य एसिन्सीओ के लिए परीक्षण लिखते समय बॉयलरप्लेट को काटने का लक्ष्य है।

कोड यहाँ रहता है: https://github.com/Martiusweb/asynctest

आप asynctest.CoroutineMock के साथ एक coroutine नकली कर सकते हैं:

>>> mock = CoroutineMock(return_value='a result') 
>>> asyncio.iscoroutinefunction(mock) 
True 
>>> asyncio.iscoroutine(mock()) 
True 
>>> asyncio.run_until_complete(mock()) 
'a result' 

यह भी side_effect विशेषता के साथ काम करता है, और एक spec के साथ एक asynctest.Mock लौट सकते हैं CoroutineMock:

>>> asyncio.iscoroutinefunction(Foo().coroutine) 
True 
>>> asyncio.iscoroutinefunction(Foo().function) 
False 
>>> asynctest.Mock(spec=Foo()).coroutine 
<class 'asynctest.mock.CoroutineMock'> 
>>> asynctest.Mock(spec=Foo()).function 
<class 'asynctest.mock.Mock'> 

unittest की सभी विशेषताएं। मॉक की उम्मीद है सही ढंग से काम करने के लिए एड (पैच(), आदि)।

1

एसिंक्रोनस बना सकते हैं अपने आप को मजाक उड़ाता है:

import asyncio 
from unittest.mock import Mock 


class AsyncMock(Mock): 

    def __call__(self, *args, **kwargs): 
     sup = super(AsyncMock, self) 
     async def coro(): 
      return sup.__call__(*args, **kwargs) 
     return coro() 

    def __await__(self): 
     return self().__await__() 
संबंधित मुद्दे