मॉकिटो का उपयोग करते हुए एक वर्ग के मॉकिंग सदस्य चर


136

मैं विकास और विशेष रूप से इकाई परीक्षणों के लिए नौसिखिया हूं। मुझे लगता है कि मेरी आवश्यकता बहुत सरल है, लेकिन मैं इस पर दूसरों के विचारों को जानने के लिए उत्सुक हूं।

मान लीजिए कि मेरे पास दो कक्षाएं हैं जैसे -

public class First {

    Second second ;

    public First(){
        second = new Second();
    }

    public String doSecond(){
        return second.doSecond();
    }
}

class Second {

    public String doSecond(){
        return "Do Something";
    }
}

मान लीजिए कि मैं परीक्षण First.doSecond()पद्धति के लिए इकाई परीक्षण लिख रहा हूं । हालाँकि, मान लीजिए, मैं मॉक Second.doSecond()क्लास को इतना पसंद करना चाहता हूं । मैं ऐसा करने के लिए मॉकिटो का उपयोग कर रहा हूं।

public void testFirst(){
    Second sec = mock(Second.class);
    when(sec.doSecond()).thenReturn("Stubbed Second");

    First first = new First();
    assertEquals("Stubbed Second", first.doSecond());
}

मैं देख रहा हूं कि मॉकिंग का असर नहीं होता है और जोर-जबरदस्ती विफल हो जाती है। क्या किसी वर्ग के सदस्य चर का मजाक उड़ाने का कोई तरीका नहीं है जिसे मैं परीक्षण करना चाहता हूं। ?

जवाबों:


86

आपको सदस्य चर तक पहुंचने का एक तरीका प्रदान करना होगा ताकि आप एक मॉक में पास हो सकें (सबसे सामान्य तरीके एक सेटर विधि या एक पैरामीटर जो एक पैरामीटर लेता है) होगा।

यदि आपका कोड ऐसा करने का एक तरीका प्रदान नहीं करता है, तो यह TDD (टेस्ट ड्रिवेन डेवलपमेंट) के लिए गलत तरीके से फैक्ट किया गया है।


4
धन्यवाद। मैँ इसे देखता हूँ। मैं बस सोच रहा हूं, मैं फिर मॉक का उपयोग करके एकीकरण परीक्षण कैसे कर सकता हूं जहां कई आंतरिक तरीके हो सकते हैं, कक्षाएं जिन्हें मॉक करने की आवश्यकता हो सकती है, लेकिन जरूरी नहीं कि हाथ से पहले एक सेटएक्सएक्सएक्सएक्सएक्स () के माध्यम से सेट किया जा सके।
आनंद हेममिज

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

9
प्रिय @kittylyst, हाँ शायद यह TDD दृष्टिकोण से या किसी भी प्रकार के तर्कसंगत दृष्टिकोण से गलत है। लेकिन कभी-कभी एक डेवलपर उन जगहों पर काम करता है जहां कुछ भी समझ में नहीं आता है और एकमात्र लक्ष्य यह है कि आपके पास केवल उन कहानियों को पूरा करना है जो आपने सौंपे हैं और चले गए हैं। हां, यह गलत है, इसका कोई मतलब नहीं है, गैर-योग्य लोग महत्वपूर्ण निर्णय लेते हैं और वे सभी चीजें। इसलिए, दिन के अंत में, विरोधी पैटर्न बहुत कुछ जीतते हैं।
अमन

1
मैं इस बारे में उत्सुक हूं, अगर किसी वर्ग के सदस्य के पास बाहरी से सेट होने का कोई कारण नहीं है, तो हमें परीक्षण के उद्देश्य से सिर्फ एक सेटर क्यों बनाना चाहिए? कल्पना कीजिए कि 'दूसरा' वर्ग वास्तव में एक फाइलसिस्टम मैनेजर या टूल है, जिसे परीक्षण के लिए ऑब्जेक्ट के निर्माण के दौरान आरम्भ किया गया है। प्रथम श्रेणी का परीक्षण करने के लिए मेरे पास सभी कारण हैं, प्रथम श्रेणी का परीक्षण करने के लिए, और इसे सुलभ बनाने के लिए शून्य कारण। मैं पायथन में यह कर सकता हूं, तो मॉकिटो के साथ क्यों नहीं?
ज़ंगदार

65

यदि आप अपना कोड नहीं बदल सकते हैं तो यह संभव नहीं है। लेकिन मुझे निर्भरता इंजेक्शन पसंद है और मॉकिटो इसका समर्थन करता है:

public class First {    
    @Resource
    Second second;

    public First() {
        second = new Second();
    }

    public String doSecond() {
        return second.doSecond();
    }
}

आपका परीक्षण:

@RunWith(MockitoJUnitRunner.class)
public class YourTest {
   @Mock
   Second second;

   @InjectMocks
   First first = new First();

   public void testFirst(){
      when(second.doSecond()).thenReturn("Stubbed Second");
      assertEquals("Stubbed Second", first.doSecond());
   }
}

यह बहुत अच्छा और आसान है।


2
मुझे लगता है कि यह अन्य लोगों की तुलना में बेहतर उत्तर है क्योंकि InjectMocks।
सूडोकोडर

यह मज़ेदार है कि कोई व्यक्ति अपने आप को एक परीक्षण नौसिखिया के रूप में कैसे प्राप्त करता है, कुछ पुस्तकालयों और रूपरेखाओं पर भरोसा करने के लिए। मैं यह सोचते गया था यह सिर्फ एक बुरा आइडिया नया स्वरूप की आवश्यकता दर्शाते हुए था ... जब तक तुम मुझे पता चला है कि यह है वास्तव में (बहुत स्पष्ट रूप से और सफाई) Mockito में संभव।
माणिक कृंतक

9
@Resource क्या है ?
इगोरगानपोलस्की

3
@IgorGanapolsky @ संसाधन जावा स्प्रिंग फ्रेमवर्क द्वारा निर्मित / उपयोग किया गया एक एनोटेशन है। स्प्रिंग को इंगित करने का इसका एक तरीका यह स्प्रिंग द्वारा प्रबंधित बीन / ऑब्जेक्ट है। stackoverflow.com/questions/4093504/resource-vs-autowired baeldung.com/spring-annotations-resource-inject-autowire यह कोई मज़ाकिया बात नहीं है, लेकिन क्योंकि इसका उपयोग गैर परीक्षण वर्ग में किया जाता है, इसलिए इसका मज़ाक उड़ाना पड़ता है। परीक्षा।
ग्री.केव

मुझे यह उत्तर समझ में नहीं आता। आप कहते हैं कि यह संभव नहीं है तो आप यह दिखा सकते हैं कि यह संभव है? क्या वास्तव में यहाँ संभव नहीं है?
गोल्डनेम

35

यदि आप अपने कोड को करीब से देखते हैं, तो आप देखेंगे कि secondआपके परीक्षण में संपत्ति अभी भी एक उदाहरण है Second, न कि मॉक (आप firstअपने कोड में मॉक पास नहीं करते हैं )।

सबसे सरल तरीका यह होगा कि कक्षा secondमें एक सेटर बनाया जाए Firstऔर इसे स्पष्ट रूप से मॉक पास किया जाए।

ऐशे ही:

public class First {

Second second ;

public First(){
    second = new Second();
}

public String doSecond(){
    return second.doSecond();
}

    public void setSecond(Second second) {
    this.second = second;
    }


}

class Second {

public String doSecond(){
    return "Do Something";
}
}

....

public void testFirst(){
Second sec = mock(Second.class);
when(sec.doSecond()).thenReturn("Stubbed Second");


First first = new First();
first.setSecond(sec)
assertEquals("Stubbed Second", first.doSecond());
}

एक और Secondउदाहरण के रूप में First'कंस्ट्रक्टर पैरामीटर' होगा।

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

public void testFirst(){
    Second sec = mock(Second.class);
    when(sec.doSecond()).thenReturn("Stubbed Second");


    First first = new First();
    Field privateField = PrivateObject.class.
        getDeclaredField("second");

    privateField.setAccessible(true);

    privateField.set(first, sec);

    assertEquals("Stubbed Second", first.doSecond());
}

लेकिन आप शायद कर सकते हैं, क्योंकि यह कोड पर परीक्षण करने के लिए दुर्लभ है जिसे आप नियंत्रित नहीं करते हैं (हालांकि एक परिदृश्य की कल्पना कर सकते हैं जहां आपको बाहरी पुस्तकालय का परीक्षण करना होगा क्योंकि यह लेखक नहीं है :))


समझ गया। मैं शायद आपके पहले सुझाव के साथ जाऊंगा।
आनंद हेममिज

बस जिज्ञासु, क्या कोई ऐसा तरीका या एपीआई है जिसके बारे में आप जानते हैं कि आवेदन स्तर या पैकेज स्तर पर किसी वस्तु / विधि का मजाक उड़ाया जा सकता है। ? मुझे लगता है कि मैं जो कह रहा हूं वह उपरोक्त उदाहरण में है जब मैं 'सेकंड' ऑब्जेक्ट का मजाक उड़ाता हूं, तो क्या ऐसा कोई तरीका है जो परीक्षण के जीवनचक्र के माध्यम से दूसरे के हर उदाहरण को ओवरराइड कर सकता है। ?
आनंद हेममिज

@AnandHemmige वास्तव में दूसरा (कंस्ट्रक्टर) क्लीनर है, क्योंकि यह अप्राकृतिक `दूसरा। उदाहरण बनाने से बचता है। आपकी कक्षाएं अच्छी तरह से इस तरह से डिकोड हो जाती हैं।
23

10
मॉकिटो आपको कुछ अच्छे एनोटेशन प्रदान करता है जिससे आप अपने मॉक को निजी चर में इंजेक्ट कर सकते हैं। एनोटेट के साथ दूसरा @Mockऔर एनोटेट के साथ पहले @InjectMocksऔर इनिशियलाइज़र में पहले को एनोटेट करें । मॉकिटो स्वचालित रूप से पहली बार दूसरी मॉक को इंजेक्ट करने के लिए एक जगह खोजने के लिए सबसे अच्छा होगा, जिसमें निजी फ़ील्ड्स शामिल हैं जो कि प्रकार से मेल खाते हैं।
१२

@Mock1.5 के आसपास था (शायद पहले, मुझे यकीन नहीं है)। 1.8.3 के @InjectMocksरूप में अच्छी तरह से शुरू की @Spyऔर @Captor
जेरिक्स

7

यदि आप सदस्य चर को नहीं बदल सकते हैं, तो इसके आस-पास का दूसरा तरीका पॉवरमॉकिट और कॉल का उपयोग करना है

Second second = mock(Second.class)
when(second.doSecond()).thenReturn("Stubbed Second");
whenNew(Second.class).withAnyArguments.thenReturn(second);

अब समस्या यह है कि नए सेकंड के लिए कोई भी कॉल उसी नकली उदाहरण को लौटा देगा। लेकिन आपके साधारण मामले में यह काम करेगा।


6

मेरे पास एक ही मुद्दा था जहां एक निजी मूल्य निर्धारित नहीं किया गया था क्योंकि मॉकिटो सुपर कंस्ट्रक्टरों को नहीं बुलाता है। यहां बताया गया है कि मैं प्रतिबिंब के साथ किस तरह से मजाक करता हूं।

सबसे पहले, मैंने एक TestUtils वर्ग बनाया जिसमें इन प्रतिबिंब विधियों सहित कई उपयोगी बर्तन शामिल हैं। प्रत्येक समय को लागू करने के लिए परावर्तन का उपयोग थोड़ा विस्की है। मैंने इन विधियों को उन परियोजनाओं पर परीक्षण करने के लिए बनाया, जो एक या किसी अन्य कारण से, कोई नकली पैकेज नहीं था और मुझे इसे शामिल करने के लिए आमंत्रित नहीं किया गया था।

public class TestUtils {
    // get a static class value
    public static Object reflectValue(Class<?> classToReflect, String fieldNameValueToFetch) {
        try {
            Field reflectField  = reflectField(classToReflect, fieldNameValueToFetch);
            reflectField.setAccessible(true);
            Object reflectValue = reflectField.get(classToReflect);
            return reflectValue;
        } catch (Exception e) {
            fail("Failed to reflect "+fieldNameValueToFetch);
        }
        return null;
    }
    // get an instance value
    public static Object reflectValue(Object objToReflect, String fieldNameValueToFetch) {
        try {
            Field reflectField  = reflectField(objToReflect.getClass(), fieldNameValueToFetch);
            Object reflectValue = reflectField.get(objToReflect);
            return reflectValue;
        } catch (Exception e) {
            fail("Failed to reflect "+fieldNameValueToFetch);
        }
        return null;
    }
    // find a field in the class tree
    public static Field reflectField(Class<?> classToReflect, String fieldNameValueToFetch) {
        try {
            Field reflectField = null;
            Class<?> classForReflect = classToReflect;
            do {
                try {
                    reflectField = classForReflect.getDeclaredField(fieldNameValueToFetch);
                } catch (NoSuchFieldException e) {
                    classForReflect = classForReflect.getSuperclass();
                }
            } while (reflectField==null || classForReflect==null);
            reflectField.setAccessible(true);
            return reflectField;
        } catch (Exception e) {
            fail("Failed to reflect "+fieldNameValueToFetch +" from "+ classToReflect);
        }
        return null;
    }
    // set a value with no setter
    public static void refectSetValue(Object objToReflect, String fieldNameToSet, Object valueToSet) {
        try {
            Field reflectField  = reflectField(objToReflect.getClass(), fieldNameToSet);
            reflectField.set(objToReflect, valueToSet);
        } catch (Exception e) {
            fail("Failed to reflectively set "+ fieldNameToSet +"="+ valueToSet);
        }
    }

}

फिर मैं इस तरह एक निजी चर के साथ कक्षा का परीक्षण कर सकता हूं। यह क्लास के पेड़ों में गहरी नकल करने के लिए उपयोगी है जिनका आपके पास कोई नियंत्रण नहीं है।

@Test
public void testWithRectiveMock() throws Exception {
    // mock the base class using Mockito
    ClassToMock mock = Mockito.mock(ClassToMock.class);
    TestUtils.refectSetValue(mock, "privateVariable", "newValue");
    // and this does not prevent normal mocking
    Mockito.when(mock.somthingElse()).thenReturn("anotherThing");
    // ... then do your asserts
}

मैंने अपने कोड को अपने वास्तविक प्रोजेक्ट से संशोधित कर यहां पृष्ठ में रखा है। एक संकलन मुद्दा या दो हो सकता है। मुझे लगता है कि आपको सामान्य विचार मिलेगा। यदि आप इसे उपयोगी पाते हैं तो कोड को हड़पने के लिए स्वतंत्र महसूस करें और इसका उपयोग करें।


क्या आप अपने कोड को वास्तविक usecase के साथ समझा सकते हैं? सार्वजनिक वर्ग की तरह tobeMocker () {निजी ClassObject classObject; } जहाँ classObject ऑब्जेक्ट को बदले जाने के लिए बराबर करता है।
जैस्पर लैंकहर्स्ट

अपने उदाहरण में, अगर ToBeMocker उदाहरण = new ToBeMocker (); और ClassObject someNewInstance = new ClassObject () {@Override // कुछ बाहरी निर्भरता की तरह}; उसके बाद TestUtils.refelctSetValue (उदाहरण के लिए, "classObject", someNewInstance); ध्यान दें कि आपको यह पता लगाना है कि आप मॉकिंग के लिए क्या ओवरराइड करना चाहते हैं। कहते हैं कि आपके पास एक डेटाबेस है और यह ओवरराइड एक मान लौटाएगा, इसलिए आपको चयन करने की आवश्यकता नहीं है। हाल ही में मेरे पास एक सेवा बस थी जिसे मैं वास्तव में संदेश को संसाधित नहीं करना चाहता था लेकिन यह सुनिश्चित करना चाहता था कि इसे प्राप्त किया जाए। इस प्रकार, मैंने निजी बस उदाहरण को इस तरह से सेट किया-सहायक?
डेव

आपको लगता होगा कि उस टिप्पणी में प्रारूपण था। इसे हटा दिया गया। इसके अलावा, यह जावा 9 के साथ काम नहीं करेगा क्योंकि यह निजी पहुंच को बंद कर देगा। आधिकारिक रिलीज होने के बाद हमें कुछ अन्य निर्माणों के साथ काम करना होगा और इसके वास्तविक सीमा के साथ काम करना होगा।
डेव

1

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

यदि आप इसे अधिक परीक्षण योग्य बनाने के लिए कोड नहीं बदल सकते हैं, तो PowerMock: https://code.google.com/p/powermock/

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

आप एक अलग मॉक रनर का उपयोग करें। और आपको उस वर्ग को तैयार करने की आवश्यकता है जो निर्माणकर्ता को आमंत्रित करने जा रहा है। (ध्यान दें कि यह एक सामान्य गोत्र है - उस वर्ग को तैयार करें जो निर्माणकर्ता को बुलाता है, न कि निर्मित वर्ग को)

@RunWith(PowerMockRunner.class)
@PrepareForTest({First.class})

फिर अपने परीक्षण सेट-अप में, आप जब निर्माण करने वाले को एक नकली वापस करने के लिए जब विधि का उपयोग कर सकते हैं

whenNew(Second.class).withAnyArguments().thenReturn(mock(Second.class));

0

हाँ, यह किया जा सकता है, निम्न परीक्षण शो के रूप में (JMockit mocking API, जिसे मैं विकसित करता हूं) के साथ लिखा गया है:

@Test
public void testFirst(@Mocked final Second sec) {
    new NonStrictExpectations() {{ sec.doSecond(); result = "Stubbed Second"; }};

    First first = new First();
    assertEquals("Stubbed Second", first.doSecond());
}

मॉकिटो के साथ, हालांकि, ऐसा परीक्षण नहीं लिखा जा सकता है। यह मॉकिटो में मॉकिंग को लागू करने के तरीके के कारण है, जहां मॉक किए जाने वाले वर्ग का एक उपवर्ग बनाया जाता है; इस "मॉक" उपवर्ग के केवल उदाहरणों का मजाक उड़ाया जा सकता है, इसलिए आपको किसी अन्य उदाहरण के बजाय परीक्षण किए गए कोड का उपयोग करने की आवश्यकता है।


3
सवाल यह नहीं था कि JMockit मॉकिटो से बेहतर है या नहीं, बल्कि मॉकिटो में इसे कैसे किया जाए। प्रतिस्पर्धा को रौंदने के अवसर की तलाश में एक बेहतर उत्पाद बनाने के लिए छड़ी!
TheZuck

8
मूल पोस्टर केवल यह कहता है कि वह मॉकिटो का उपयोग कर रहा है; यह केवल निहित है कि मॉकिटो एक निश्चित और कठोर आवश्यकता है ताकि संकेत मिले कि जेमॉकिट इस स्थिति को संभाल सकता है जो अनुचित नहीं है।
बॉम्बे

0

यदि आप मॉकिटो में स्प्रिंग से ReflectionTestUtils का विकल्प चाहते हैं , तो उपयोग करें

Whitebox.setInternalState(first, "second", sec);

ढेर अतिप्रवाह में आपका स्वागत है! अन्य उत्तर हैं जो ओपी के प्रश्न प्रदान करते हैं, और वे कई साल पहले पोस्ट किए गए थे। एक उत्तर पोस्ट करते समय, कृपया सुनिश्चित करें कि आप या तो एक नया समाधान जोड़ते हैं, या काफी बेहतर व्याख्या करते हैं, खासकर जब पुराने प्रश्नों का उत्तर दे रहे हैं या अन्य उत्तरों पर टिप्पणी करते हैं।
help-info.de
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.