मॉकिटो मैचर्स कैसे काम करते हैं?


122

Mockito तर्क matchers (जैसे any, argThat, eq, same, और ArgumentCaptor.capture()) बहुत अलग ढंग Hamcrest matchers से व्यवहार करते हैं।

  • मॉकिटो मैचर्स अक्सर InvalidUseOfMatchersException का कारण बनते हैं, यहां तक ​​कि कोड में भी जो किसी भी मैचर्स का उपयोग करने के बाद लंबे समय तक निष्पादित होता है।

  • मॉकिटो मैचर्स अजीब नियमों के लिए निपुण हैं, जैसे कि सभी तर्कों के लिए केवल मॉकिटो मैचर्स के उपयोग की आवश्यकता होती है अगर किसी दिए गए तरीके में एक तर्क एक मैचर का उपयोग करता है।

  • मॉकिटो मेचर्स NullPointerException का कारण बन सकता है जब Answerएस को ओवरराइड करते समय या उपयोग करते समय (Integer) any()आदि।

  • कुछ तरीकों से मॉकिटो मैचर्स के साथ रिफैक्टिंग कोड अपवाद और अप्रत्याशित व्यवहार पैदा कर सकता है, और पूरी तरह से विफल हो सकता है।

मॉकिटो मैचर्स को इस तरह से क्यों बनाया गया है, और उन्हें कैसे लागू किया जाता है?

जवाबों:


236

मॉकिटो मैचर्स स्टैटिक मेथड हैं और उन तरीकों पर कॉल करते हैं, जो कॉल टू और के दौरान बहस के लिए खड़े होते हैंwhenverify

हैमरेस्ट मैचर्स (संग्रहीत संस्करण) (या हैमरेस्ट-स्टाइल मैचर्स) स्टेटलेस, सामान्य-उद्देश्य ऑब्जेक्ट इंस्टेंस हैं जो लागू करते हैं Matcher<T>और एक विधि को उजागर करते हैं matches(T)जो ऑब्जेक्ट मैचर्स के मानदंडों से मेल खाता है। वे साइड इफेक्ट से मुक्त होने का इरादा रखते हैं, और आम तौर पर नीचे दिए गए जैसे कथनों में उपयोग किया जाता है।

/* Mockito */  verify(foo).setPowerLevel(gt(9000));
/* Hamcrest */ assertThat(foo.getPowerLevel(), is(greaterThan(9000)));

मॉकिटो मैचर्स मौजूद हैं, हैमरेस्ट-शैली के मैचर्स से अलग, ताकि मिलान अभिव्यक्तियों का वर्णन सीधे विधि इनवॉइस में फिट हो : मॉकिटो मैचर्स वापस लौटते हैं Tजहां हैमरेस्ट मैचर तरीके मैचर ऑब्जेक्ट्स (प्रकार का Matcher<T>) वापस करते हैं।

Mockito matchers जैसे स्थिर तरीकों के माध्यम से लागू कर रहे हैं eq, any, gt, और startsWithपर org.mockito.Matchersऔर org.mockito.AdditionalMatchers। एडाप्टर्स भी हैं, जो मॉकिटो संस्करणों में बदल गए हैं:

  • मॉकिटो 1.x के लिए, Matchersकुछ कॉल में (जैसे intThatया जैसे argThat) मॉकिटो मैचर्स होते हैं जो हैमरेस्ट मैचर्स को सीधे मापदंडों के रूप में स्वीकार करते हैं। ArgumentMatcher<T>विस्तारित org.hamcrest.Matcher<T>, जिसका उपयोग आंतरिक हैमरेस्ट प्रतिनिधित्व में किया गया था और यह किसी भी प्रकार के मॉकिटो मैचर के बजाय हैमरेस्ट मैचर बेस क्लास था ।
  • मॉकिटो 2.0+ के लिए, मॉकिटो का अब हैमरेस्ट पर सीधा निर्भरता नहीं है। Matchersकॉल किए गए ऑब्जेक्ट्स के रूप में intThatया ऐसे ऑब्जेक्ट्स को argThatरैप करते ArgumentMatcher<T>हैं जो अब लागू org.hamcrest.Matcher<T>नहीं होते हैं लेकिन समान तरीकों से उपयोग किए जाते हैं। Hamcrest एडेप्टर जैसे argThatऔर intThatअभी भी उपलब्ध हैं, लेकिन MockitoHamcrestइसके बजाय चले गए हैं ।

चाहे मैचर्स हैमरेस्ट हों या बस हैमरेस्ट-स्टाइल, उन्हें इस तरह से अनुकूलित किया जा सकता है:

/* Mockito matcher intThat adapting Hamcrest-style matcher is(greaterThan(...)) */
verify(foo).setPowerLevel(intThat(is(greaterThan(9000))));

उपरोक्त कथन में: foo.setPowerLevelएक विधि है जो एक को स्वीकार करती है intis(greaterThan(9000))एक रिटर्न देता है Matcher<Integer>, जो एक setPowerLevelतर्क के रूप में काम नहीं करेगा । Mockito मिलान intThatलपेटता है कि Hamcrest शैली Matcher और रिटर्न एक intऐसा कर सकते हैं एक तर्क के रूप दिखाई देते हैं; gt(9000)उदाहरण के कोड की पहली पंक्ति में, मॉकिटो मैचर्स उस संपूर्ण अभिव्यक्ति को एक कॉल में लपेट देंगे।

मैचर्स क्या करते / करते हैं

when(foo.quux(3, 5)).thenReturn(true);

जब तर्क मिलानकर्ताओं का उपयोग नहीं करते हैं, तो मॉकिटो आपके तर्क मूल्यों को रिकॉर्ड करता है और उनकी तुलना उनके equalsतरीकों से करता है।

when(foo.quux(eq(3), eq(5))).thenReturn(true);    // same as above
when(foo.quux(anyInt(), gt(5))).thenReturn(true); // this one's different

जब आप किसी माचिस को पसंद करते हैं anyया gt(इससे बड़ा) कहते हैं, तो मॉकिटो एक माचिस वस्तु को संग्रहित करता है जिसके कारण मॉकिटो उस समानता को छोड़ देता है और आपकी पसंद के मैच को लागू करता है। मामले में argumentCaptor.capture()यह एक मिलानकर्ता को संग्रहीत करता है जो बाद के निरीक्षण के लिए इसके तर्क को बचाता है।

मिलानकर्ता डमी मानों जैसे कि शून्य, खाली संग्रह, या वापस लौटाते हैं null। मॉकिटो एक सुरक्षित, उपयुक्त डमी मूल्य वापस करने की कोशिश करता है, जैसे कि 0 anyInt()या any(Integer.class)उसके List<String>लिए खाली anyListOf(String.class)। प्रकार के क्षरण के कारण, हालांकि, मॉकिटो में किसी भी मूल्य को वापस करने के लिए टाइप जानकारी का अभाव है, लेकिन या जिसके nullलिए एक NullPointerException कारण हो सकता है यदि "ऑटो-अनबॉक्स" को एक आदिम मान देने की कोशिश की जाती है।any()argThat(...)null

जैसे matchers eqऔर gtले पैरामीटर मान; आदर्श रूप से, स्टबिंग / सत्यापन शुरू होने से पहले इन मूल्यों की गणना की जानी चाहिए। दूसरे कॉल को मॉकिंग के बीच में एक मॉक कॉल करना स्टबिंग के साथ हस्तक्षेप कर सकता है।

मिलान विधियों का उपयोग रिटर्न मान के रूप में नहीं किया जा सकता है; उदाहरण के लिए, वाक्यांश thenReturn(anyInt())या thenReturn(any(Foo.class))मॉकिटो में कोई रास्ता नहीं है । मॉकिटो को यह जानने की जरूरत है कि स्टबिंग कॉल में वापस आने के लिए कौन सा उदाहरण है, और आपके लिए एक मनमाना रिटर्न मान का चयन नहीं करेगा।

कार्यान्वयन का विवरण

मिलान ArgumentMatcherStorage नामक एक वर्ग में निहित स्टैक में हैमरेस्ट-स्टाइल ऑब्जेक्ट मैचर्स के रूप में संग्रहीत किए जाते हैं । MockitoCore और Matchers प्रत्येक का खुद का एक ThreadSafeMockingProgress उदाहरण है, जिसमें स्टेटिक रूप से एक थ्रॉक्लॉक होल्डिंग MockingProgress इंस्टेंस है। यह एक MockingProgressImpl है जो एक ठोस ArgumentMatcherStorageImpl रखती है । नतीजतन, मॉक और मैचर स्टेट स्थिर है लेकिन मॉकिटो और मैचर्स वर्गों के बीच लगातार थ्रेड-स्कोप किया जाता है।

अधिकांश मिलानकर्ता कॉल केवल इस स्टैक में जोड़ते हैं and,ornot जैसे मैचर्स और , के लिए एक अपवाद । यह पूरी तरह से जावा के मूल्यांकन क्रम से मेल खाता है (और निर्भर करता है) , जो एक तेज तरीके को लागू करने से पहले बाएं से दाएं तर्कों का मूल्यांकन करता है:

when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6]      [5]  [1]       [4] [2]     [3]

यह करेगा:

  1. anyInt()स्टैक में जोड़ें ।
  2. gt(10)स्टैक में जोड़ें ।
  3. lt(20)स्टैक में जोड़ें ।
  4. निकालें gt(10)और lt(20)जोड़ें and(gt(10), lt(20))
  5. कॉल करें foo.quux(0, 0), जो (जब तक अन्यथा स्टब न हो) डिफ़ॉल्ट मान लौटाता है falsequux(int, int)सबसे हालिया कॉल के रूप में आंतरिक रूप से मॉकिटो निशान ।
  6. कॉल when(false), जो अपने तर्क को खारिज करता है और quux(int, int)5. पहचान की गई स्टब विधि को तैयार करता है । केवल दो वैध अवस्थाएं स्टैक लंबाई 0 (समानता) या 2 (मैचर्स) के साथ हैं, और स्टैक पर दो मैचर्स हैं (चरण 1 और 4), इसलिए मॉकिटो any()अपने पहले तर्क के लिए और and(gt(10), lt(20))दूसरे तर्क के लिए एक मिलानकर्ता के साथ विधि को स्टब्स करता है और स्टैक को साफ़ करता है।

यह कुछ नियमों को प्रदर्शित करता है:

  • मॉकिटो quux(anyInt(), 0)और के बीच अंतर नहीं बता सकता है quux(0, anyInt())। वे दोनों quux(0, 0)स्टैक पर एक इंट मैचर के साथ एक कॉल की तरह दिखते हैं । नतीजतन, यदि आप एक मिलानकर्ता का उपयोग करते हैं, तो आपको सभी तर्कों का मिलान करना होगा।

  • कॉल ऑर्डर केवल महत्वपूर्ण नहीं है, यह वही है जो यह सब काम करता है । वैरिएबल को वेरिएबल्स में निकालना आमतौर पर काम नहीं करता है, क्योंकि यह आमतौर पर कॉल ऑर्डर बदलता है। विधियों के लिए मैचर्स निकालना, हालांकि, महान काम करता है।

    int between10And20 = and(gt(10), lt(20));
    /* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);
    // Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().
    
    public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }
    /* OK */  when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);
    // The helper method calls the matcher methods in the right order.
  • स्टैक अक्सर बदलता है कि मॉकिटो इसे बहुत सावधानी से पुलिस नहीं कर सकता है। यह केवल स्टैक की जांच कर सकता है जब आप मॉकिटो या मॉक के साथ बातचीत करते हैं, और मैचर्स को यह जानने के बिना स्वीकार करना पड़ता है कि क्या वे तुरंत उपयोग किए जाते हैं या गलती से छोड़ दिए जाते हैं। सिद्धांत रूप में, स्टैक हमेशा कॉल करने के लिए whenया के बाहर खाली होना चाहिए verify, लेकिन मॉकिटो स्वचालित रूप से जांच नहीं कर सकता है। आप मैन्युअल रूप से देख सकते हैं Mockito.validateMockitoUsage()

  • कॉल करने के लिए when, मॉकिटो वास्तव में प्रश्न में विधि को कॉल करता है, जो एक अपवाद को फेंक देगा यदि आपने अपवाद फेंकने के लिए विधि (या गैर-शून्य या गैर-शून्य मानों की आवश्यकता होती है) को फेंक दिया है। doReturnऔर doAnswer(आदि) वास्तविक विधि को लागू नहीं करते हैं और अक्सर एक उपयोगी विकल्प होते हैं।

  • यदि आपने स्टबिंग के बीच एक मॉक विधि को बुलाया था (जैसे एक eqमाचिस के लिए एक उत्तर की गणना करने के लिए ), तो मॉकिटो उस कॉल के बजाय स्टैक की लंबाई की जांच करेगा , और संभवत: विफल हो जाएगा।

  • यदि आप कुछ बुरा करने की कोशिश करते हैं, जैसे कि अंतिम विधि को रोकने / सत्यापित करने के लिए , मॉकिटो वास्तविक विधि को बुलाएगा और स्टैक पर अतिरिक्त मिलान भी छोड़ देगाfinalविधि कॉल एक अपवाद फेंक नहीं सकते हैं, लेकिन आप एक मिल सकता है InvalidUseOfMatchersException आवारा matchers एक नकली के साथ जब आप अगली सहभागिता से।

सामान्य समस्यायें

  • अमान्य यूओफ़ऑफ़मैटर्स अपवाद :

    • जांचें कि हर एक तर्क में बिल्कुल एक मिलानकर्ता कॉल है, यदि आप मैचर्स का उपयोग करते हैं, और यह कि आपने किसी मिलानकर्ता को कॉल whenया verifyकॉल के बाहर उपयोग नहीं किया है । मैचर्स को कभी भी स्टेबल रिटर्न वैल्यू या फील्ड / वैरिएबल के रूप में इस्तेमाल नहीं करना चाहिए।

    • जांचें कि आप एक मॉक को एक मिलान तर्क प्रदान करने के हिस्से के रूप में नहीं बुला रहे हैं।

    • जांचें कि आप एक मिलानकर्ता के साथ अंतिम विधि को स्टब / सत्यापित करने का प्रयास नहीं कर रहे हैं। यह स्टैक पर एक मिलानकर्ता को छोड़ने का एक शानदार तरीका है, और जब तक कि आपकी अंतिम विधि एक अपवाद नहीं फेंकती है, यह केवल एक बार हो सकता है जब आपको पता चलता है कि आप जिस विधि का मजाक उड़ा रहे हैं वह अंतिम है।

  • आदिम तर्कों के साथ NullPointerException: (Integer) any() रिटर्न करते समय अशक्त any(Integer.class); यह एक कारण हो सकता है NullPointerExceptionअगर आप intएक पूर्णांक के बजाय उम्मीद कर रहे हैं । किसी भी स्थिति में, पसंद करें anyInt(), जो शून्य पर लौटेगा और ऑटो-बॉक्सिंग चरण को भी छोड़ देगा।

  • NullPointerException या अन्य अपवाद: कॉल when(foo.bar(any())).thenReturn(baz)वास्तव में कॉल करेंगे foo.bar(null), जिसे आपने एक अशक्त तर्क प्राप्त करते समय अपवाद फेंकने के लिए ठूंसा होगा। doReturn(baz).when(foo).bar(any()) ठूंसे हुए व्यवहार को छोड़ देना

सामान्य समस्या निवारण

  • MockitoJUnitRunner का उपयोग करें , या स्पष्ट रूप validateMockitoUsageसे अपने tearDownया @Afterविधि में कॉल करें (जो धावक आपके लिए स्वचालित रूप से करेगा)। यह निर्धारित करने में मदद करेगा कि आपने मिलानकर्ताओं का दुरुपयोग किया है या नहीं।

  • डिबगिंग उद्देश्यों के लिए, validateMockitoUsageसीधे अपने कोड में कॉल जोड़ें । यदि आपके पास स्टैक पर कुछ भी है, तो यह फेंक देगा, जो एक बुरे लक्षण की एक अच्छी चेतावनी है।


2
इस राइटअप के लिए धन्यवाद। जब / तब रीटर्न प्रारूप के साथ एक NullPointerException मुझे समस्याएँ पैदा कर रहा था, जब तक कि मैंने इसे doReturn या कब में बदल दिया।
yngwietiger

11

जेफ बोमन के उत्कृष्ट जवाब के लिए बस एक छोटा सा जोड़, जैसा कि मैंने अपनी समस्याओं में से एक के समाधान के लिए खोज करते समय यह प्रश्न पाया:

यदि किसी विधि का whenकॉल एक से अधिक मॉक प्रशिक्षित कॉल से मेल खाता है , तो कॉल का क्रम whenमहत्वपूर्ण है, और यह सबसे व्यापक से सबसे विशिष्ट तक होना चाहिए। जेफ के उदाहरणों में से एक से शुरू:

when(foo.quux(anyInt(), anyInt())).thenReturn(true);
when(foo.quux(anyInt(), eq(5))).thenReturn(false);

वह आदेश है जो सुनिश्चित करता है (शायद) वांछित परिणाम:

foo.quux(3 /*any int*/, 8 /*any other int than 5*/) //returns true
foo.quux(2 /*any int*/, 5) //returns false

यदि आप कॉल करते समय उलटा करते हैं तो परिणाम हमेशा होगा true


2
हालांकि यह उपयोगी जानकारी है, यह स्टबिंग का संबंध है , मैचर्स का नहीं है , इसलिए यह इस प्रश्न पर कोई मतलब नहीं हो सकता है। ऑर्डर मायने रखता है, लेकिन इसमें केवल अंतिम-परिभाषित मिलान श्रृंखला जीतती है : इसका मतलब है कि सह-अस्तित्व वाले स्टब्स को अक्सर सबसे विशिष्ट से कम से कम घोषित किया जाता है, लेकिन कुछ मामलों में आप एक ही परीक्षण के मामले में विशेष रूप से नकली व्यवहार के बहुत व्यापक ओवरराइड कर सकते हैं। जिस बिंदु पर एक व्यापक परिभाषा को अंतिम रूप से आने की आवश्यकता हो सकती है।
जेफ बोमन

1
@JeffBowman मैंने सोचा था कि यह इस सवाल पर समझ में आता है क्योंकि सवाल मॉकिटो मैचर्स और मैचर्स के बारे में है जब स्टबिंग (आपके अधिकांश उदाहरणों में) किया जा सकता है। एक स्पष्टीकरण के लिए Google पर खोज करने के बाद से मुझे यह सवाल मिला कि मुझे लगता है कि यह जानकारी यहाँ होना उपयोगी है।
tibtof
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.