कुछ तरीकों का मजाक उड़ाने के लिए मॉकिटो का इस्तेमाल करें लेकिन दूसरों का नहीं


402

क्या कोई तरीका है, मॉकिटो का उपयोग करके, क्लास में कुछ तरीकों का मजाक उड़ाने के लिए, लेकिन दूसरों के लिए नहीं?

उदाहरण के लिए, इस (वैसे काल्पनिक) में Stockकक्षा मैं नकली करना चाहते हैं getPrice()और getQuantity()(जैसा कि नीचे परीक्षण स्निपेट में दिखाया गया है) वापसी मान, लेकिन मैं चाहता हूँ getValue()के रूप में में कोडित गुणा करने Stockवर्ग

public class Stock {
  private final double price;
  private final int quantity;

  Stock(double price, int quantity) {
    this.price = price;
    this.quantity = quantity;
  }

  public double getPrice() {
    return price;
  }

  public int getQuantity() {
    return quantity;
  }
  public double getValue() {
    return getPrice() * getQuantity();
  }

  @Test
  public void getValueTest() {
    Stock stock = mock(Stock.class);
    when(stock.getPrice()).thenReturn(100.00);
    when(stock.getQuantity()).thenReturn(200);
    double value = stock.getValue();
    // Unfortunately the following assert fails, because the mock Stock getValue() method does not perform the Stock.getValue() calculation code.
    assertEquals("Stock value not correct", 100.00*200, value, .00001);
}

4
आप ऐसा क्यों करना चाहते हो? आपको या तो कक्षा का परीक्षण करना चाहिए (किस स्थिति में, बिल्कुल भी मज़ाक नहीं होना चाहिए) या आपको एक अलग वर्ग (जिस स्थिति में, कोई कार्यक्षमता नहीं है) का परीक्षण करते समय उसका मज़ाक उड़ाना चाहिए। आप आंशिक आघात क्यों करेंगे?
वेल्टरंपिरैट

3
ठीक है, यह वास्तविक चीज़ का एक छोटा सा उदाहरण है। हकीकत में, मैं डेटाबेस से एक कॉल से बचने की कोशिश कर रहा हूं, जो विरोधाभासी मूल्यों से गुजर रहा है, लेकिन मैं यह सत्यापित करना चाहता हूं कि अन्य तरीके उन वंचित मूल्यों के साथ सही तरीके से काम करते हैं। क्या ऐसा करने के लिए इससे अच्छा तरीका है?
विक्टर ग्राज़ी

5
निश्चित रूप से: अपने डेटाबेस कॉल को एक अलग वर्ग में ले जाएं (डोमेन लॉजिक और डेटाबेस एक्सेस एक ही क्लास में नहीं होना चाहिए; वे दो अलग-अलग चिंताएं हैं), इसके इंटरफेस को निकालें, डोमेन लॉजिक क्लास से कनेक्ट करने के लिए उस इंटरफेस का उपयोग करें, और केवल मॉक करें। परीक्षण के दौरान इंटरफ़ेस।
वेल्टरंपिरैट

1
मैं पूरी तरह से सहमत हूं, पूरी तस्वीर की व्याख्या करना मुश्किल है बिना कोड के gobs अपलोड किए बिना, यहां थर्ड पार्टी लाइब्रेरी भी शामिल है।
विक्टर ग्राज़ी

1
आप शायद कर सकते थे। लेकिन फिर, यह "ऐसा करने का एक बेहतर तरीका" नहीं होगा: आपका डेटाबेस कोड एक कार्यान्वयन विवरण है जिसे आप अपने शेष एप्लिकेशन से छिपाना चाहते हैं, शायद एक अलग पैकेज में भी चले जाएं। क्या आप अगली बार अपने सीक्वल स्टेटमेंट को बदलने के लिए अपने डोमेन लॉजिक को फिर से जोड़ना नहीं चाहेंगे?
वेल्टरमम्पिरैट

जवाबों:


642

अपने सवाल का सीधा जवाब देने के लिए, हां, आप दूसरों का मजाक उड़ाए बिना कुछ तरीकों का मजाक उड़ा सकते हैं। इसे आंशिक मॉक कहा जाता है । आंशिक मॉक पर मॉकिटो प्रलेखन देखें अधिक जानकारी के लिए।

अपने उदाहरण के लिए, आप अपने परीक्षण में निम्नलिखित कुछ कर सकते हैं:

Stock stock = mock(Stock.class);
when(stock.getPrice()).thenReturn(100.00);    // Mock implementation
when(stock.getQuantity()).thenReturn(200);    // Mock implementation
when(stock.getValue()).thenCallRealMethod();  // Real implementation

उस स्थिति में, प्रत्येक विधि कार्यान्वयन का मजाक उड़ाया जाता है, जब तक thenCallRealMethod()कि when(..)खंड में निर्दिष्ट नहीं किया जाता है।

एक संभावना यह भी है कि मॉक के बजाय जासूस के साथ अन्य तरीके से भी :

Stock stock = spy(Stock.class);
when(stock.getPrice()).thenReturn(100.00);    // Mock implementation
when(stock.getQuantity()).thenReturn(200);    // Mock implementation
// All other method call will use the real implementations

उस मामले में, सभी विधि कार्यान्वयन वास्तविक हैं, सिवाय इसके कि क्या आपने एक नकली व्यवहार को परिभाषित किया है when(..)

जब आप when(Object)पिछले उदाहरण में जासूसी के साथ उपयोग करते हैं तो एक महत्वपूर्ण नुकसान होता है । वास्तविक विधि को कहा जाएगा (क्योंकि रनटाइम stock.getPrice()से पहले मूल्यांकन किया जाता when(..)है)। यह एक समस्या हो सकती है यदि आपके तरीके में तर्क हैं जिन्हें नहीं कहा जाना चाहिए। आप पिछले उदाहरण को इस तरह लिख सकते हैं:

Stock stock = spy(Stock.class);
doReturn(100.00).when(stock).getPrice();    // Mock implementation
doReturn(200).when(stock).getQuantity();    // Mock implementation
// All other method call will use the real implementations

एक और संभावना का उपयोग करने के लिए हो सकता है org.mockito.Mockito.CALLS_REAL_METHODS, जैसे:

Stock MOCK_STOCK = Mockito.mock( Stock.class, CALLS_REAL_METHODS );

यह वास्तविक कार्यान्वयन के लिए अनस्टब कॉल को दर्शाता है।


हालाँकि, आपके उदाहरण के साथ, मेरा मानना ​​है कि यह अभी भी विफल हो जाएगा, क्योंकि इसके कार्यान्वयन पर getValue()निर्भर करता है quantityऔर price, इसके बजाय getQuantity()और getPrice(), जो आपने चौंक दिया है।

एक और संभावना यह है कि पूरी तरह से नकली से बचने के लिए:

@Test
public void getValueTest() {
    Stock stock = new Stock(100.00, 200);
    double value = stock.getValue();
    assertEquals("Stock value not correct", 100.00*200, value, .00001);
}

21
मुझे लगता है कि यह जवाब गलत है। आपको ऑब्जेक्ट का एक उदाहरण SPY करने की आवश्यकता है, न कि क्लास को MOCK करने के लिए।
GaRRaPeTa

2
@GRRaPeTa मैं कहूंगा कि जासूसी और मजाक करना दोनों ही उचित विकल्प हैं। यह कहना मुश्किल है कि इस मामले के लिए सबसे अच्छा क्या है, क्योंकि ओपी कहता है कि यह एक सरल उदाहरण है।
जॉन न्यूमूंस

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

2
Stock stock = spy(Stock.class);यह गलत लगता है, spyविधि केवल वस्तुओं को स्वीकार करने लगती है न कि कक्षाओं को।
परमवीर सिंह करवाल

4
+1 के बीच के अंतर को इंगित करने के लिए doReturn(retval).when(spyObj).methodName(args)औरwhen(spyObj.methodName(args)).thenReturn(retval)
कप्तान_ओब जाहिर

140

मॉकिटो में स्पाई के माध्यम से एक वर्ग के आंशिक मॉकिंग का भी समर्थन किया जाता है

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

//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);

//using the spy calls real methods
spy.add("one");
spy.add("two");

//size() method was stubbed - 100 is printed
System.out.println(spy.size());

विस्तृत विवरण के लिए 1.10.19और 2.7.22डॉक्स की जाँच करें ।


37

डॉक्स के अनुसार :

Foo mock = mock(Foo.class, CALLS_REAL_METHODS);

// this calls the real implementation of Foo.getSomething()
value = mock.getSomething();

when(mock.getSomething()).thenReturn(fakeValue);

// now fakeValue is returned
value = mock.getSomething();

2
यह प्रदर्शित करने के लिए धन्यवाद कि एक नकली कैसे सेट करें जहां परीक्षण से वास्तविक नियंत्रण को सभी तरीकों के लिए कहा जाता है, कुछ को छोड़कर मुझे नियंत्रित करने की आवश्यकता है।
bigh_29

class NaughtyLinkedList extends LinkedList { public int size() { throw new RuntimeException("don't call me");} } @Test public void partialMockNaughtLinkedList(){ List mock = mock(NaughtyLinkedList.class, CALLS_REAL_METHODS); mock.add(new Object()); // this calls the real function when(mock.size()).thenReturn(2); // For whatever reason, this lines throws the RuntimeException. assertEquals(2,mock.size()); }यह काम नहीं करता है। जो भी कारण के लिए, जब "जब" निष्पादित होता है, तो यह वास्तव में उस विधि को निष्पादित करता है जिसे नकली माना जाता है। कोड:
लांस काइंड

3
समस्या "जब" है। "जब" वास्तव में उस चीज को निष्पादित करेगा जिसे आप आंशिक रूप से नकली करना चाहते हैं। इससे बचने के लिए एक विकल्प है: doReturn ()। DoReturn () को docs.mockito.googlecode.com/hg/1.9.5/org/mockito/…
Lance Kind

18

आप जो चाहते हैं वह org.mockito.Mockito.CALLS_REAL_METHODSडॉक्स के अनुसार है:

/**
 * Optional <code>Answer</code> to be used with {@link Mockito#mock(Class, Answer)}
 * <p>
 * {@link Answer} can be used to define the return values of unstubbed invocations.
 * <p>
 * This implementation can be helpful when working with legacy code.
 * When this implementation is used, unstubbed methods will delegate to the real implementation.
 * This is a way to create a partial mock object that calls real methods by default.
 * <p>
 * As usual you are going to read <b>the partial mock warning</b>:
 * Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects.
 * How does partial mock fit into this paradigm? Well, it just doesn't... 
 * Partial mock usually means that the complexity has been moved to a different method on the same object.
 * In most cases, this is not the way you want to design your application.
 * <p>
 * However, there are rare cases when partial mocks come handy: 
 * dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.)
 * However, I wouldn't use partial mocks for new, test-driven & well-designed code.
 * <p>
 * Example:
 * <pre class="code"><code class="java">
 * Foo mock = mock(Foo.class, CALLS_REAL_METHODS);
 *
 * // this calls the real implementation of Foo.getSomething()
 * value = mock.getSomething();
 *
 * when(mock.getSomething()).thenReturn(fakeValue);
 *
 * // now fakeValue is returned
 * value = mock.getSomething();
 * </code></pre>
 */

इस प्रकार आपका कोड इस तरह दिखना चाहिए:

import org.junit.Test;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

public class StockTest {

    public class Stock {
        private final double price;
        private final int quantity;

        Stock(double price, int quantity) {
            this.price = price;
            this.quantity = quantity;
        }

        public double getPrice() {
            return price;
        }

        public int getQuantity() {
            return quantity;
        }

        public double getValue() {
            return getPrice() * getQuantity();
        }
    }

    @Test
    public void getValueTest() {
        Stock stock = mock(Stock.class, withSettings().defaultAnswer(CALLS_REAL_METHODS));
        when(stock.getPrice()).thenReturn(100.00);
        when(stock.getQuantity()).thenReturn(200);
        double value = stock.getValue();

        assertEquals("Stock value not correct", 100.00 * 200, value, .00001);
    }
}

कॉल करने के लिए Stock stock = mock(Stock.class);कॉल org.mockito.Mockito.mock(Class<T>)जो इस तरह दिखता है:

 public static <T> T mock(Class<T> classToMock) {
    return mock(classToMock, withSettings().defaultAnswer(RETURNS_DEFAULTS));
}

मान के डॉक्स RETURNS_DEFAULTSबताते हैं:

/**
 * The default <code>Answer</code> of every mock <b>if</b> the mock was not stubbed.
 * Typically it just returns some empty value. 
 * <p>
 * {@link Answer} can be used to define the return values of unstubbed invocations. 
 * <p>
 * This implementation first tries the global configuration. 
 * If there is no global configuration then it uses {@link ReturnsEmptyValues} (returns zeros, empty collections, nulls, etc.)
 */

1
अच्छी तरह से देखा ... लेकिन क्या मैं पूछ सकता हूं कि आप ऐसा क्यों करते हैं withSettings()...? ऐसा प्रतीत होता है कि org.mockito.internal.stubbing.answers.CallsRealMethods()(उदाहरण के लिए) नौकरी कर सकता है ... और इस वर्ग के लिए javadoc विशेष रूप से कहता है कि यह आंशिक मोक्स के उपयोग के लिए है ...
माइक कृंतक

3
इसके अलावा ... यह अन्य उत्तरों द्वारा सामने आई समस्या में नहीं चलेगा: अर्थात thenReturnवास्तव में इस विधि को निष्पादित करेगा (जो समस्याओं का कारण हो सकता है, हालांकि इस उदाहरण में नहीं), और doReturnइस तरह के मामले में बेहतर है ...?
माइक कृंतक

4

मॉकिटो की जासूसी विधि का उपयोग करके आंशिक रूप से मजाक करना आपकी समस्या का समाधान हो सकता है, जैसा कि पहले से ही ऊपर दिए गए उत्तरों में बताया गया है। कुछ हद तक मैं इस बात से सहमत हूँ कि, आपके ठोस उपयोग के मामले में, DB लुक को मॉक करने के लिए अधिक उपयुक्त हो सकता है। मेरे अनुभव से यह हमेशा संभव नहीं है - कम से कम अन्य वर्कअराउंड के बिना नहीं - कि मैं बहुत बोझिल या कम नाजुक होने पर विचार करूंगा। ध्यान दें, कि आंशिक मॉकिंग मॉकिटो के सहयोगी संस्करणों के साथ काम नहीं करता है। आप कम से कम 1.8.0 का उपयोग करें।

मैंने इस उत्तर को पोस्ट करने के बजाय मूल प्रश्न के लिए एक सरल टिप्पणी लिखी होगी, लेकिन StackOverflow इसकी अनुमति नहीं देता है।

बस एक और बात: मैं वास्तव में यह नहीं समझ सकता कि कई बार एक सवाल पूछा जा रहा है यहाँ "क्यों आप ऐसा करना चाहते हैं" टिप्पणी के साथ कम से कम समस्या को समझने की कोशिश किए बिना। Escp तौर पर जब यह आता है तो आंशिक रूप से मॉकिंग की आवश्यकता होती है वास्तव में बहुत सारे उपयोग के मामले हैं जो मैं सोच सकता था कि यह कहां उपयोगी होगा। इसीलिए मॉकिटो के लोगों ने उस कार्यक्षमता को प्रदान किया। इस सुविधा का अत्यधिक उपयोग नहीं किया जाना चाहिए। लेकिन जब हम परीक्षण केस सेटअप के बारे में बात करते हैं जो अन्यथा बहुत जटिल तरीके से स्थापित नहीं किया जा सकता है, तो जासूसी का उपयोग किया जाना चाहिए।


2
मुझे लगता है कि यह जवाब आंशिक रूप से एक राय है। कृपया संपादन पर विचार करें।

2
परिवार में नए सदस्य को खुश करने के लिए तैयार किया गया। यह -ve ज़ोन प्राप्त करने की आवश्यकता नहीं है, वास्तव में तकनीकी रूप से गलत या गलत भाषा / टोन में कुछ भी नहीं है। नए सदस्यों पर दया करें। धन्यवाद।
सौरभ पाटिल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.