मॉकिटो: विधि की जासूसी करने की कोशिश मूल विधि कह रही है


351

मैं मॉकिटो 1.9.0 का उपयोग कर रहा हूं। मैं JUnit परीक्षा में कक्षा की एकल पद्धति के लिए व्यवहार का मजाक बनाना चाहता हूं, इसलिए मेरे पास है

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

समस्या यह है कि दूसरी पंक्ति में, myClassSpy.method1()वास्तव में कहा जा रहा है, जिसके परिणामस्वरूप एक अपवाद है। एकमात्र कारण जो मैं मोक्स का उपयोग कर रहा हूं वह यह है कि बाद में, जब भी myClassSpy.method1()कहा जाता है, वास्तविक विधि को नहीं बुलाया जाएगा और myResultsऑब्जेक्ट वापस कर दिया जाएगा।

MyClassएक इंटरफ़ेस है और myInstanceयह उस का एक कार्यान्वयन है, यदि वह मायने रखता है।

इस जासूसी व्यवहार को सही करने के लिए मुझे क्या करने की आवश्यकता है?


इस पर एक नज़र डालें: stackoverflow.com/a/29394497/355438
Lu55

जवाबों:


610

मुझे आधिकारिक दस्तावेज उद्धृत करने दें :

वास्तविक वस्तुओं की जासूसी करने पर महत्वपूर्ण पकड़!

कभी-कभी जासूसों को ठगने के लिए (वस्तु) का उपयोग करना असंभव है। उदाहरण:

List list = new LinkedList();
List spy = spy(list);

// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

आपके मामले में यह कुछ इस तरह है:

doReturn(resulstIWant).when(myClassSpy).method1();

27
क्या होगा यदि मैं इस पद्धति का उपयोग करता हूं और मेरे मूल एक को बुलाया जा रहा है? क्या मेरे द्वारा पारित मापदंडों के साथ समस्या हो सकती है? यहाँ पूरा परीक्षण है: pastebin.com/ZieY790P send विधि कहा जा रहा है
इवगेनी पेट्रोव

26
@EvgeniPetrov अगर आपका मूल तरीका अभी भी कहा जा रहा है तो शायद यह है क्योंकि आपका मूल तरीका अंतिम है। मॉकिटो अंतिम तरीकों का मजाक नहीं उड़ाता है, और आपको अंतिम तरीकों के मजाक के बारे में चेतावनी नहीं दे सकता है।
21

1
क्या यह doThrow () के लिए भी संभव है?
गोबलींस

1
हाँ, दुर्भाग्य से स्थिर तरीके अचूक और "अन-स्पाई-सक्षम" हैं। मैं स्टैटिक विधियों से निपटने के लिए क्या करता हूं, स्टैटिक कॉल के चारों ओर एक विधि को लपेटना और उस विधि पर doNothing या doReturn का उपयोग करना। सिंगलेट्स या स्कैला वस्तुओं के साथ मैं तर्क के मांस को एक अमूर्त वर्ग में ले जाता हूं और इससे मुझे ऑब्जेक्ट के वैकल्पिक टेस्ट क्लास से संबंधित होने की क्षमता मिलती है जिससे मैं एक जासूस बना सकता हूं।
एंड्रयू नॉर्मन

24
और क्या होगा अगर नहीं अंतिम और नहीं स्थिर विधि अभी भी कहा जा रहा है?
एक्स-ह्यूमन

27

मेरा मामला स्वीकृत जवाब से अलग था। मैं एक उदाहरण के लिए एक पैकेज-निजी पद्धति का मजाक उड़ाने की कोशिश कर रहा था जो उस पैकेज में नहीं थी

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

और परीक्षण कक्षाएं

package common;

public abstract class AnimalTest<T extends Animal> {
  @Before
  setup(){
    doNothing().when(getInstance()).packageProtected();
  }

  abstract T getInstance();
}

package instances;

class DogTest extends AnimalTest<Dog> {
  Dog getInstance(){
    return spy(new Dog());
  }

  @Test
  public void myTest(){}
}

संकलन सही है, लेकिन जब यह परीक्षण को सेटअप करने का प्रयास करता है, तो यह इसके बजाय वास्तविक विधि को लागू करता है।

संरक्षित विधि की घोषणा या सार्वजनिक समस्या को हल करता है, यह एक स्वच्छ समाधान नहीं है।


2
मैं एक समान मुद्दे में भाग गया, लेकिन परीक्षण और पैकेज-निजी पद्धति एक ही पैकेज में थे। मुझे लगता है कि शायद मॉकिटो में सामान्य रूप से पैकेज-निजी तरीकों के साथ मुद्दे हैं।
डेव

22

मेरे मामले में, मॉकिटो 2.0 का उपयोग करते हुए, मुझे वास्तविक कॉल को स्टब करने के लिए सभी any()मापदंडों को बदलना पड़ा nullable()


2
ऐसा मत करो कि 321 वोट किया गया शीर्ष उत्तर आपको नीचे मिल जाए, इससे मेरी समस्या हल हो गई :) मैं इसके लिए कुछ घंटों से संघर्ष कर रहा हूं!
क्रिस केसेल

3
यह मेरे लिए जवाब था। उन लोगों के लिए इसे और भी आसान बनाने के लिए जो आपकी विधि का मजाक उड़ाते समय अनुसरण करते हैं: foo = Mockito.spy(foo); Mockito.doReturn(someValue).when(foo).methodToPrevent(nullable(ArgumentType.class));
Stryder

मॉकिटो 2.23.4 के साथ मैं पुष्टि कर सकता हूं कि यह आवश्यक नहीं है, यह anyऔर eqमैचर्स के साथ ठीक काम करता है ।
vmaldosan

2
2.23.4 कार्यकारी संस्करण पर तीन अलग-अलग तरीकों से कोशिश की: कोई भी (), eq () और अशक्त ()। केवल बाद में काम किया
rhzhman

नमस्ते, आपका समाधान वास्तव में अच्छा है और मेरे लिए भी काम किया है। धन्यवाद
धीरेन सोलंकी

16

टॉमाज़ नर्कविक्ज़ का जवाब पूरी कहानी नहीं बताता है!

एनबी मॉकिटो संस्करण: 1.10.19।

मैं एक बहुत Mockito newb हूं, इसलिए निम्न व्यवहार की व्याख्या नहीं कर सकता: अगर वहाँ एक विशेषज्ञ है जो इस उत्तर को बेहतर बना सकता है, तो कृपया स्वतंत्र महसूस करें।

यहाँ प्रश्न में विधि getContentStringValue, नहीं है final और नहीं है static

यह लाइन मूल विधि को कॉल करती है getContentStringValue:

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));

इस लाइन को मूल विधि नहीं कहते हैं getContentStringValue:

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));

जिन कारणों के लिए मैं उत्तर नहीं दे सकता, isA()उन उद्देश्यों का उपयोग करके (?) "कॉल न करें" पद्धति doReturnविफल होने का व्यवहार करती है ।

आइए यहां शामिल विधि हस्ताक्षर देखें: वे दोनों के staticतरीके हैं Matchers। दोनों को जावदोक द्वारा लौटने के लिए कहा जाता है null, जो अपने आप में अपने सिर को चारों ओर ले जाना थोड़ा मुश्किल है। संभवतः Classपैरामीटर के रूप में पास की गई वस्तु की जांच की जाती है, लेकिन परिणाम की गणना या तो कभी नहीं की जाती या खारिज नहीं की जाती है। यह देखते हुए कि nullआप किसी भी वर्ग के लिए खड़े हो सकते हैं और यह कि आप मॉकड विधि के न होने की उम्मीद कर रहे हैं, क्या हस्ताक्षर नहीं कर सकते हैं isA( ... )और जेनेरिक पैरामीटर के बजाय any( ... )सिर्फ * लौटा सकते हैं ?null<T>

वैसे भी:

public static <T> T isA(java.lang.Class<T> clazz)

public static <T> T any(java.lang.Class<T> clazz)

एपीआई प्रलेखन इस बारे में कोई सुराग नहीं देता है। यह इस तरह की "कॉल न करने की विधि" की आवश्यकता को भी कहता है "व्यवहार" बहुत दुर्लभ है। व्यक्तिगत रूप से मैं हर समय इस तकनीक का उपयोग करता हूं : आम तौर पर मुझे पता चलता है कि मॉकिंग में कुछ पंक्तियां शामिल हैं जो "दृश्य सेट करें" ... इसके बाद एक विधि को कॉल करके जो मॉक संदर्भ में उस दृश्य को "बाहर खेलता है" जिसे आपने मंचित किया है .. । और जब आप दृश्यों को सेट कर रहे हैं और आखिरी चीज़ जो आप चाहते हैं, वह है अभिनेताओं के लिए मंच छोड़ कर प्रवेश करना और अपने दिलों का अभिनय शुरू करना ...

लेकिन यह मेरे वेतन ग्रेड से परे है ... मैं किसी भी पासिंग मॉकिटो उच्च पुजारी से स्पष्टीकरण आमंत्रित करता हूं ...

* "जेनेरिक पैरामीटर" सही शब्द है?


मुझे नहीं पता कि यह स्पष्टता को जोड़ता है या आगे मामले को भ्रमित करता है, लेकिन isA () और किसी भी () के बीच का अंतर यह है कि वास्तव में प्रकार की जाँच होती है, जबकि किसी भी () तरीकों का परिवार केवल टाइप कास्टिंग से बचने के लिए बनाया गया था बहस।
केविन वेलकर

@KevinWelker धन्यवाद। और वास्तव में विधि के नामों में एक निश्चित आत्म-व्याख्यात्मक गुणवत्ता की कमी नहीं है। मैं हालांकि, और हालांकि हल्के ढंग से, पर्याप्त रूप से दस्तावेज नहीं करने के लिए जीनियस मॉकिटो डिजाइनरों के साथ मुद्दा उठाता हूं। कोई शक नहीं कि मुझे मॉकिटो की एक और किताब पढ़ने की जरूरत है। PS वास्तव में "मध्यवर्ती मॉकिटो" सिखाने के लिए बहुत कम संसाधन हैं!
माइक कृंतक

1
इतिहास यह है कि anyXX तरीकों को पहले केवल टाइपकास्टिंग से निपटने के तरीके के रूप में बनाया गया था। फिर जब यह सुझाव दिया गया कि वे तर्क जाँच जोड़ते हैं, तो वे मौजूदा API के उपयोगकर्ताओं को तोड़ना नहीं चाहते थे, इसलिए उन्होंने isA () परिवार बनाया। यह जानकर कि किसी भी () तरीकों को सभी के साथ टाइपिंग की जाँच करनी चाहिए थी, उन्होंने इसे बदलने में देरी की जब तक कि उन्होंने मॉकिटो 2.X ओवरहाल में अन्य ब्रेकिंग परिवर्तन नहीं पेश किए (जो मैंने अभी तक कोशिश नहीं की है)। 2.x + में, anyX () विधियां isA () विधियों के लिए उपनाम हैं।
केविन वेलकर

धन्यवाद। यह हम में से एक ही समय में कई पुस्तकालय अद्यतन कर रहे लोगों के लिए एक निर्णायक जवाब है, क्योंकि कोड जो चलाने के लिए उपयोग किया जाता है वह अचानक और मौन विफल रहा है।
डेक्स स्टैकर

6

एक और संभावित परिदृश्य जो जासूसों के साथ समस्या पैदा कर सकता है, वह है जब आप स्प्रिंग बीन्स (स्प्रिंग टेस्ट फ्रेमवर्क के साथ) या कुछ अन्य ढांचे का परीक्षण कर रहे हैं जो परीक्षण के दौरान आपकी वस्तुओं की जांच कर रहे हैं

उदाहरण

@Autowired
private MonitoringDocumentsRepository repository

void test(){
    repository = Mockito.spy(repository)
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

ऊपर दिए गए कोड में स्प्रिंग और मॉकिटो दोनों आपके मॉनिटरिंगडिजाइनमेंट्स रिपॉजिटरी ऑब्जेक्ट को प्रॉक्सी करने की कोशिश करेंगे, लेकिन स्प्रिंग पहले होगा, जिससे फाइंडमोनिट्रिंगडेंडकॉमेंट्स मेथड का असली कॉल आएगा। अगर हम रिपॉजिटरी ऑब्जेक्ट पर स्पाई डालने के बाद अपना कोड डिबग करते हैं तो यह डीबगर के अंदर इस तरह दिखेगा:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@ बचाव के लिए SpyBean

यदि @Autowiredएनोटेशन के बजाय हम एनोटेशन का उपयोग करते @SpyBeanहैं, तो हम उपरोक्त समस्या का समाधान करेंगे, स्पाईबीन एनोटेशन रिपॉजिटरी ऑब्जेक्ट को भी इंजेक्ट करेगा लेकिन यह सबसे पहले मॉकिटो द्वारा अनुमानित होगा और डीबगर के अंदर इस तरह दिखेगा

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

और यहाँ कोड है:

@SpyBean
private MonitoringDocumentsRepository repository

void test(){
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

1

मैंने अभी तक जासूस को मूल विधि को कॉल करने का एक और कारण पाया है।

किसी ने एक finalवर्ग का मजाक उड़ाने का विचार किया था , और इसके बारे में पाया MockMaker:

चूंकि यह हमारे मौजूदा तंत्र के लिए अलग तरह से काम करता है और इसकी अलग-अलग सीमाएँ हैं और जैसा कि हम अनुभव और उपयोगकर्ता प्रतिक्रिया को इकट्ठा करना चाहते हैं, यह सुविधा उपलब्ध होने के लिए स्पष्ट रूप से सक्रिय होना था; यह src/test/resources/mockito-extensions/org.mockito.plugins.MockMakerसिंगल लाइन वाली फाइल बनाकर मॉकिटो एक्सटेंशन तंत्र के माध्यम से किया जा सकता है :mock-maker-inline

स्रोत: https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

जब मैंने विलय किया और उस फ़ाइल को अपनी मशीन में लाया, तो मेरे परीक्षण विफल हो गए।

मुझे बस लाइन (या फ़ाइल) को निकालना था, और spy()काम किया।


मेरे मामले में यही कारण था, मैं एक अंतिम विधि का मखौल उड़ाने की कोशिश कर रहा था, लेकिन यह स्पष्ट त्रुटि संदेश के बिना वास्तविक को कॉल करता रहा जो भ्रमित था।
बशर अली लबादी

1

पार्टी के लिए थोड़ा देर से लेकिन ऊपर के समाधान मेरे लिए काम नहीं करते थे, इसलिए मेरे 0.02 $ को साझा करना

मोकिटो संस्करण: 1.10.19

MyClass.java

private int handleAction(List<String> argList, String action)

Test.java

MyClass spy = PowerMockito.spy(new MyClass());

निम्नलिखित ने मेरे लिए काम नहीं किया (वास्तविक विधि कहा जा रहा था):

1।

doReturn(0).when(spy , "handleAction", ListUtils.EMPTY_LIST, new String());

2।

doReturn(0).when(spy , "handleAction", any(), anyString());

3।

doReturn(0).when(spy , "handleAction", null, null);

निम्नलिखित काम:

doReturn(0).when(spy , "handleAction", any(List.class), anyString());

0

यह सुनिश्चित करने का एक तरीका नहीं है कि एक वर्ग से एक विधि कहा जाता है, एक डमी के साथ विधि को ओवरराइड करना है।

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
            @Override
            public void select(TreeItem i) {
                log.debug("SELECT");
            };
        });

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.