मॉकिटो केवल सुपरक्लास की एक विधि की कॉल को कैसे मॉक करें


94

मैं कुछ परीक्षणों में मॉकिटो का उपयोग कर रहा हूं।

मेरे पास निम्न वर्ग हैं:

class BaseService {  
    public void save() {...}  
}

public Childservice extends BaseService {  
    public void save(){  
        //some code  
        super.save();
    }  
}   

मैं केवल दूसरी कॉल ( super.save) का मजाक बनाना चाहता हूं ChildService। पहली कॉल वास्तविक विधि को कॉल करना चाहिए। क्या ऐसा करने के लिए कोई रास्ता है?


क्या इसे पॉवरमॉकिटो के साथ हल किया जा सकता है?
javaPlease42

@ javaPlease42: हाँ आप कर सकते हैं: stackoverflow.com/a/23884011/2049986
जैकब वैन लिंगेन

जवाबों:


57

नहीं, मॉकिटो इसका समर्थन नहीं करता है।

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

वंशानुक्रम पर अनुकूल रचना

यदि आप एक सुपर क्लास निकालने के बजाय एक रणनीति निकालते हैं तो समस्या दूर हो गई है।

यदि फिर भी आपको कोड बदलने की अनुमति नहीं है, लेकिन आपको इसे वैसे भी परीक्षण करना होगा, और इस अजीब तरीके से, अभी भी उम्मीद है। कुछ AOP टूल (उदाहरण के लिए AspectJ) के साथ आप सुपर क्लास विधि में कोड को बुनाई कर सकते हैं और इसके निष्पादन से पूरी तरह से बच सकते हैं (yuck)। यह काम नहीं करता है यदि आप परदे के पीछे का उपयोग कर रहे हैं, तो आपको बाइटकोड मॉडिफिकेशन (या तो समय बुनाई या लोड समय संकलन) का उपयोग करना होगा। ऐसे नकली ढांचे हैं जो इस तरह की चाल का समर्थन करते हैं, जैसे पावरमॉक और पावरमॉकिटो।

मेरा सुझाव है कि आप रिफैक्टरिंग के लिए जाएं, लेकिन यदि आप एक विकल्प नहीं हैं तो आप कुछ गंभीर हैकिंग मज़ा के लिए हैं।


5
मैं एलएसपी उल्लंघन नहीं देख रहा हूं। मेरे पास ओपी के रूप में लगभग एक ही सेटअप है: एक आधार डीएओ वर्ग जिसमें एक खोजक () विधि है, और एक उपवर्ग डीएओ है जो सुपर.फेडएएल () को कॉल करके आधार विधि को ओवरराइड करता है और फिर परिणाम को सॉर्ट करता है। उपवर्ग सभी संदर्भों में सुपरक्लैस को स्वीकार करने योग्य है। क्या मैं आपके अर्थ को गलत समझ रहा हूं?

1
मैं एलएसपी टिप्पणी को हटा दूंगा (यह जवाब में मूल्य नहीं जोड़ता है)।
इविन

हां इनहेरिटेंस बेकार है और मैं जिस बेवकूफ ढांचे के साथ फंस गया हूं उसे एकमात्र विकल्प के रूप में विरासत के साथ डिजाइन किया गया है।
श्रीधर सरनोबत

मान लें कि आप सुपरक्लास को फिर से डिज़ाइन नहीं कर सकते हैं, तो आप //some codesकोड को एक ऐसी विधि में निकाल सकते हैं जिसे अलग से परीक्षण किया जा सकता है।
फसमल

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

86

यदि आपके पास वास्तव में रिफैक्टिंग के लिए कोई विकल्प नहीं है तो आप सुपर मेथड कॉल जैसे हर चीज़ का मज़ाक / ठोक सकते हैं

    class BaseService {

        public void validate(){
            fail(" I must not be called");
        }

        public void save(){
            //Save method of super will still be called.
            validate();
        }
    }

    class ChildService extends BaseService{

        public void load(){}

        public void save(){
            super.save();
            load();
        }
    }

    @Test
    public void testSave() {
        ChildService classToTest = Mockito.spy(new ChildService());

        // Prevent/stub logic in super.save()
        Mockito.doNothing().when((BaseService)classToTest).validate();

        // When
        classToTest.save();

        // Then
        verify(classToTest).load();
    }

2
यह कोड वास्तव में super.save () इनवोकेशन अधिकार को नहीं रोक सकेगा, इसलिए यदि आप super.save () में बहुत कुछ करते हैं, तो आपको उन सभी कॉलों को रोकना होगा ...
iwein

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

14
यह तब तक अच्छा काम करता है जब तक कि वैरिडेट छिपा न हो या सेव विधि दूसरे तरीके से कॉल करने के बजाय सीधे काम करती हो। mockito नहीं करता है: Mockito.doNothing ()। जब ((बेससेवा) जासूस) .save (); यह 'आधार सेवा को बचाने के लिए नहीं, लेकिन बच्चे को बचाने पर सेवा करेगा :(
tibi

मुझे यह काम करने के लिए नहीं मिल सकता है - यह बच्चे की विधि को भी रोकता है। BaseServiceअमूर्त है, हालांकि मैं यह नहीं देखता कि यह प्रासंगिक क्यों होगा।
श्रीधर सरनोबत

1
@ श्रीधर-सरनोबत हां, मैं दिखाई दे रही है एक ही बात :( किसी को भी पता है कि कैसे बाहर केवल ठूंठ को इसे पाने के लिए super.validate()?
stantonk

4

ChildService.save () विधि से अलग विधि के लिए कोड को फिर से दर्शाने पर विचार करें और ChildService.save () परीक्षण के बजाय नई विधि का परीक्षण करें, इस तरह से आप अनावश्यक सुपर कॉल सुपर विधि से बचेंगे।

उदाहरण:

class BaseService {  
    public void save() {...}  
}

public Childservice extends BaseService {  
    public void save(){  
        newMethod();    
        super.save();
    }
    public void newMethod(){
       //some codes
    }
} 

1

सुपर क्लास मेथड को कॉल करने वाली सब क्लास मे एक पैकेज प्रोटेक्टेड (एक ही पैकेज मे टेस्ट क्लास मानती है) मेथड बनाये और फिर उस मेथड को आपके ओवरराइड सब क्लास मेथड मे बुलाए। आप जासूसी पैटर्न के उपयोग के माध्यम से अपने परीक्षण में इस पद्धति पर अपेक्षाएं निर्धारित कर सकते हैं। अपने परीक्षण में सुपर विधि के लिए सभी अपेक्षा सेटिंग से निपटने के लिए सुंदर लेकिन निश्चित रूप से बेहतर नहीं है


क्या मैं यह भी कह सकता हूं कि वंशानुक्रम से अधिक रचना लगभग हमेशा बेहतर होती है, लेकिन कभी-कभी इसका सरलता से उपयोग करना सरल होता है। जब तक जावा एक बेहतर रचनात्मक मॉडल को शामिल नहीं करता है, जैसे कि स्कैला या ग्रूवी, यह हमेशा मामला होगा और यह मुद्दा मौजूद रहेगा
ल्यूक

1

यहां तक ​​कि अगर मैं iwein प्रतिक्रिया से पूरी तरह सहमत हूं (

वंशानुक्रम पर अनुकूल रचना

), मैं मानता हूं कि कुछ समय के लिए विरासत स्वाभाविक है, और मुझे लगता है कि इसे तोड़ना या केवल एक इकाई परीक्षण की खातिर रिफ्लेक्टर नहीं लगता।

तो, मेरा सुझाव:

/**
 * BaseService is now an asbtract class encapsulating 
 * some common logic callable by child implementations
 */
abstract class BaseService {  
    protected void commonSave() {
        // Put your common work here
    }

    abstract void save();
}

public ChildService extends BaseService {  
    public void save() {
        // Put your child specific work here
        // ...

        this.commonSave();
    }  
}

और फिर, इकाई परीक्षण में:

    ChildService childSrv = Mockito.mock(ChildService.class, Mockito.CALLS_REAL_METHODS);

    Mockito.doAnswer(new Answer<Void>() {
        @Override
        public Boolean answer(InvocationOnMock invocation)
                throws Throwable {
            // Put your mocked behavior of BaseService.commonSave() here
            return null;
        }
    }).when(childSrv).commonSave();

    childSrv.save();

    Mockito.verify(childSrv, Mockito.times(1)).commonSave();

    // Put any other assertions to check child specific work is done

0

कारण यह है कि आपका आधार वर्ग सार्वजनिक-एड नहीं है, तो मॉकिटो दृश्यता के कारण इसे बाधित नहीं कर सकता है, यदि आप आधार वर्ग को सार्वजनिक के रूप में बदलते हैं, या @ उप-वर्ग (सार्वजनिक रूप में) में, तो मॉकिटो इसे सही तरीके से मॉक कर सकता है।

public class BaseService{
  public boolean foo(){
    return true;
  }
}

public ChildService extends BaseService{
}

@Test
@Mock ChildService childService;
public void testSave() {
  Mockito.when(childService.foo()).thenReturn(false);

  // When
  assertFalse(childService.foo());
}

8
यह बात नहीं है। चाइल्ड सर्विस को फू () को ओवरराइड करना चाहिए और समस्या यह है कि बेसस्वेस्क.फू () को कैसे मॉक किया जाए लेकिन चाइल्डसेवा.फू ()
एड्रियन कोस्टर

0

शायद सबसे आसान विकल्प अगर विरासत समझ में आता है, तो सुपर को कॉल करने के लिए एक नई विधि (पैकेज प्राइवेट ??) बनाने की अनुमति देता है (इसे सुपरफिंडल कहते हैं), वास्तविक उदाहरण की जासूसी करें और फिर जिस तरह से आप नकली करना चाहते हैं, उस तरीके से सुपरफाइंडऑल () विधि का मजाक उड़ाएं। मूल वर्ग एक। यह कवरेज और दृश्यता के मामले में सही समाधान नहीं है, लेकिन इसे काम करना चाहिए और इसे लागू करना आसान है।

 public Childservice extends BaseService {
    public void save(){
        //some code
        superSave();
    }

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