क्या यह मॉकिटो के रीसेट विधि का उचित उपयोग है?


68

मेरे पास मेरे परीक्षण वर्ग में एक निजी पद्धति है जो आमतौर पर उपयोग की जाने वाली Barवस्तु का निर्माण करती है । Barनिर्माता कहता है someMethod()मेरा मज़ाक उड़ाया वस्तु में विधि:

private @Mock Foo mockedObject; // My mocked object
...

private Bar getBar() {
  Bar result = new Bar(mockedObject); // this calls mockedObject.someMethod()
}

मेरे कुछ परीक्षण तरीकों में मैं someMethodउस विशेष परीक्षण द्वारा भी जाँच करना चाहता था। कुछ इस प्रकार है:

@Test
public void someTest() {
  Bar bar = getBar();

  // do some things

  verify(mockedObject).someMethod(); // <--- will fail
}

यह विफल रहता है, क्योंकि नकली वस्तु ने someMethodदो बार आह्वान किया था । मैं अपने परीक्षण के तरीकों को अपने तरीके के दुष्प्रभावों के बारे में परवाह नहीं करना चाहता हूं getBar(), इसलिए क्या मेरी नकली वस्तु को अंत में रीसेट करना उचित होगा getBar()?

private Bar getBar() {
  Bar result = new Bar(mockedObject); // this calls mockedObject.someMethod()
  reset(mockedObject); // <-- is this OK?
}

मैं पूछता हूं, क्योंकि प्रलेखन का सुझाव है कि नकली वस्तुओं को रीसेट करना आमतौर पर खराब परीक्षणों का संकेत है। हालाँकि, यह मुझे ठीक लगता है।

विकल्प

वैकल्पिक विकल्प कॉलिंग लगता है:

verify(mockedObject, times(2)).someMethod();

मेरी राय में, प्रत्येक परीक्षा को getBar()बिना किसी लाभ के , की उम्मीदों के बारे में जानने के लिए मजबूर करता है ।

जवाबों:


60

मेरा मानना है कि इस मामले हैं, जहां का उपयोग कर में से एक है reset()ठीक है। आप जो परीक्षण लिख रहे हैं वह परीक्षण कर रहा है कि "कुछ चीजें" एकल कॉल को ट्रिगर करती हैं someMethod()verify()किसी भी अलग संख्या के आह्वान के साथ बयान लिखना भ्रम की स्थिति पैदा कर सकता है।

  • atLeastOnce() झूठी सकारात्मक के लिए अनुमति देता है, जो एक बुरी बात है क्योंकि आप चाहते हैं कि आपके परीक्षण हमेशा सही हों।
  • times(2)झूठे सकारात्मक को रोकता है, लेकिन ऐसा लगता है जैसे आप कह रहे हैं कि "मुझे पता है कि निर्माणकर्ता एक जोड़ता है" के बजाय दो आह्वान की उम्मीद कर रहे हैं। इसके अलावा, अगर एक अतिरिक्त कॉल को जोड़ने के लिए कंस्ट्रक्टर में कुछ परिवर्तन होता है, तो परीक्षण में अब झूठे सकारात्मक के लिए एक मौका है। और कॉल को हटाने से परीक्षण विफल हो जाएगा क्योंकि परीक्षण अब गलत है जो परीक्षण किया जा रहा है इसके बजाय गलत है।

reset()सहायक विधि में उपयोग करके , आप इन दोनों मुद्दों से बचते हैं। हालाँकि, आपको सावधान रहने की आवश्यकता है कि यह आपके द्वारा किए गए किसी भी स्टबिंग को भी रीसेट कर देगा, इसलिए चेतावनी दी जाए। reset()रोकने के लिए हतोत्साहित करने का प्रमुख कारण है

bar = mock(Bar.class);
//do stuff
verify(bar).someMethod();
reset(bar);
//do other stuff
verify(bar).someMethod2();

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


17
मेरी इच्छा है कि मॉकिटो ने रिसेटइंटरट्रेस्स () कॉल प्रदान की, जो सत्यापन (..., टाइम्स) (...) के उद्देश्य के लिए पिछले इंटरैक्शन को भूलने के लिए और स्टबिंग रखने के लिए कहें। यह {सेटअप की परीक्षण स्थितियों को बनाएगा; कार्य; सत्यापित;} इससे निपटने के लिए बहुत आसान है। यह {सेटअप होगा; resetInteractions; कार्य; सत्यापित करें}
अर्कादि

2
वास्तव में मॉकिटो 2.1 के बाद से यह स्टब्स को रीसेट किए बिना इनवॉइस क्लियर करने का एक तरीका प्रदान करता है:Mockito.clearInvocations(T... mocks)
कॉलिन डी बेनेट

6

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

इसके बजाय reset(), सरल छोटे लिखने पर विचार और लंबा, पर निर्दिष्ट परीक्षणों पर परीक्षण तरीकों ध्यान केंद्रित करें। पहली संभावित कोड गंध reset()परीक्षण विधि के बीच में है।

मॉकिटो डॉक्स से निकाला गया ।

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

अगर आपको वास्तव में इसकी परवाह नहीं है, तो आप इसका उपयोग कर सकते हैं:

verify(mockedObject, atLeastOnce()).someMethod();

ध्यान दें कि यह अंतिम गलत परिणाम दे सकता है, यदि आप getBar से कुछMethod को कॉल करते हैं, और इसके बाद नहीं (यह एक गलत व्यवहार है, लेकिन परीक्षण विफल नहीं होगा)।


2
हां, मैंने वह सटीक उद्धरण देखा है (मैंने इसे अपने प्रश्न से जोड़ा है)। वर्तमान में मैं अभी तक एक अच्छा तर्क नहीं देख पा रहा हूं कि ऊपर मेरा उदाहरण "खराब" क्यों है। आप एक आपूर्ति कर सकते हैं?
डंकन जोन्स

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

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

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

जब मैं कई चीजों का परीक्षण करना चाहता हूं, तो मुझे आमतौर पर यह एक कठिन चीज लगती है, लेकिन जॉनीट केवल परीक्षणों को पैरामीटर करने के लिए कोई अच्छा (!) तरीका नहीं देता है। एनयूनाइट के विपरीत, उदाहरण के लिए एनोटेशन के साथ होता है।
स्टीफन हेंड्रिक्स

3

बिलकुल नहीं। जैसा कि अक्सर होता है, एक स्वच्छ परीक्षण लिखने में आपको जो कठिनाई हो रही है वह आपके उत्पादन कोड के डिजाइन के बारे में एक प्रमुख लाल झंडा है। इस मामले में, सबसे अच्छा उपाय यह है कि आप अपने कोड को रिफलेक्टर करें ताकि बार का निर्माता किसी भी तरीके को न बुलाए।

कन्स्ट्रक्टर्स को निर्माण करना चाहिए, तर्क को निष्पादित नहीं करना चाहिए। विधि का रिटर्न मान लें और इसे एक निर्माता पैरामीटर के रूप में पास करें।

new Bar(mockedObject);

हो जाता है:

new Bar(mockedObject.someMethod());

यदि इससे कई स्थानों पर इस तर्क का दोहराव होता है, तो एक फैक्ट्री पद्धति बनाने पर विचार करें जिसे आपके बार ऑब्जेक्ट के स्वतंत्र रूप से परीक्षण किया जा सकता है:

public Bar createBar(MockedObject mockedObject) {
    Object dependency = mockedObject.someMethod();
    // ...more logic that used to be in Bar constructor
    return new Bar(dependency);
}

यदि यह रीफैक्टरिंग बहुत कठिन है, तो रीसेट () का उपयोग करना एक अच्छा काम है। लेकिन आइए स्पष्ट हों - यह संकेत दे रहा है कि आपका कोड खराब तरीके से डिज़ाइन किया गया है।

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