यह जांचने का सबसे अच्छा तरीका क्या है कि Mockito के साथ सही तर्क सही जगह पर आता है?

अगले इकाई परीक्षण पर विचार करें:

@Test
public void getProjectByIdTest() {
    Long projectId = 1L;
    ProjectEntity expectedProject = mock(ProjectEntity.class);
    when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);

    assertThat(projectService.getById(projectId), is(expectedProject));
    verify(projectRepository).findOne(projectId);
}

यहां हम जांचते हैं कि projectService अपने तर्क को verify के साथ स्पष्ट रूप से सही जगह पर भेजता है।

अब इस इकाई-परीक्षण की जाँच करें:

@Test
public void getProjectByIdTest() {
    Long projectId = 1L;
    ProjectEntity expectedProject = mock(ProjectEntity.class);
    when(projectRepository.findOne(projectId)).thenReturn(expectedProject);

    assertThat(projectService.getById(projectId), is(expectedProject));
}

यह यह भी जांचता है कि projectService ने अपने तर्क को सही जगह पर पारित किया है, लेकिन परोक्ष रूप से, when के साथ (इसलिए यदि projectService वास्तव में कुछ यादृच्छिक संख्या को projectRepository.findOne(), assertThat विफल हो जाएगा क्योंकि नकली गलत मान लौटाएगा)।

तो यह कैसे किया जाना चाहिए? मुझे ऐसा लगता है कि इसके बिना verify यह परीक्षण कुछ स्पष्टता खो देता है; लेकिन दूसरी ओर से यह छोटा है।

1
Roman Golyshev 20 जिंदा 2017, 20:32

3 जवाब

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

@Test
public void getProjectByIdTest_withVerify() {
    Long projectId = 1L;
    ProjectEntity expectedProject = mock(ProjectEntity.class);

    // Your simulated dependency returns expectedProject for every ID?
    when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);

    assertThat(projectService.getById(projectId), is(expectedProject));

    // Is it a requirement of your test that this method must be called?
    // Maybe your system someday calls a "findAll" method, or caches values.
    verify(projectRepository).findOne(projectId);
}

तुलना करना:

@Test
public void getProjectByIdTest() {
    Long projectId = 1L;
    ProjectEntity expectedProject = mock(ProjectEntity.class);

    // You return the expectedProject when asked for. Everything else returns null.
    when(projectRepository.findOne(projectId)).thenReturn(expectedProject);

    // You check that the return value is correct, which implies the call succeeded.    
    assertThat(projectService.getById(projectId), is(expectedProject));
}

verify इंटरैक्शन निश्चित रूप से अपना स्थान रखते हैं, विशेष रूप से ArgumentCaptors के लिए और उन इंटरैक्शन के लिए जहां एक विशेष विधि कॉल अनुबंध का हिस्सा है, जैसे सर्वर या RPC कॉल या शून्य विधि कॉल।

शाब्दिक-आधिकारिक दृष्टिकोण के लिए, लेख पढ़ें "क्या पूछने में कोई अंतर है और बता रहे हैं?" मॉकिटो के प्रवर्तक स्ज़ेपन फैबर द्वारा। यह इस बारे में बहुत विस्तार और अंतर्दृष्टि देता है कि अपेक्षाओं से क्या अनुमान लगाया जा सकता है बनाम सत्यापन योग्य क्या है।

1
Jeff Bowman 21 जिंदा 2017, 23:39
क्या यह आपके परीक्षण की आवश्यकता है कि इस विधि को कॉल किया जाना चाहिए? हो सकता है कि आपका सिस्टम किसी दिन "findAll" विधि कहे, या मानों को संचित करे। ठीक है, अगर मेरा कार्यान्वयन बदल जाएगा, तो दोनों परीक्षण विधियों को भी बदलना होगा। मुझे लगता है कि मैं अपने और अपने स्वयं के कार्यान्वयन का परीक्षण कर रहा हूं, क्योंकि मैं अलगाव में अपनी कक्षा का परीक्षण करना चाहता हूं और इसे करने का दूसरा तरीका नहीं देख सकता। यदि मेरे पास वास्तविक डेटाबेस (एकीकरण परीक्षण) है, तो मैं केवल यह परीक्षण कर पाऊंगा कि projectService.getById(id) अपेक्षित वस्तु लौटाता है जिसे मैंने स्वयं डेटाबेस में रखा है (और मैं findOne के बजाय >findAll)।
 – 
Roman Golyshev
22 जिंदा 2017, 00:15
1
महान लेख, मुझ पर शर्म की बात है कि मुझे यह स्वयं नहीं मिला! If I stub then it is verified for free, so I don’t verify. If I verify then I don’t care about the return value, so I don’t stub. मुझे लगता है कि यह वही है जो @davidxxx ने अपने उत्तर में कहा था।
 – 
Roman Golyshev
22 जिंदा 2017, 00:21
आपका स्वागत है! ध्यान रखें कि परीक्षण के लिए यह आदर्श है कि कार्यान्वयन में परिवर्तन होने पर इसे बदलने की आवश्यकता नहीं है। हालांकि मॉकिटो अतिरिक्त स्टब्स की आवश्यकता के कारण उसमें हस्तक्षेप करता है, फिर भी आप कार्यान्वयन के बजाय अनुबंध के आधार पर अपना परीक्षण लिख सकते हैं।
 – 
Jeff Bowman
22 जिंदा 2017, 00:38

छोटी सी टिप्पणी। आपकी मॉकिटो व्यवहार रिकॉर्डिंग आपके दो उदाहरणों के बीच सममित नहीं हैं:

when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);

तथा

when(projectRepository.findOne(projectId)).thenReturn(expectedProject);

आप दोनों ही मामलों में when(projectRepository.findOne(projectId)) का उपयोग करके देखें कि आप अपने दावे कैसे करते हैं।

तो यह कैसे किया जाना चाहिए? मुझे ऐसा लगता है कि इसके सत्यापन के बिना यह परीक्षण कुछ स्पष्टता खो देता है; लेकिन दूसरी ओर से यह छोटा है।

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

यदि नकली निर्भरता की विधि कुछ लौटाती है, जैसा कि आपके मामले में है, तो दूसरा समाधान इकाई परीक्षण के दृष्टिकोण से बहुत अधिक स्वाभाविक लगता है।

जब आप ऐसा कुछ लिखते हैं:

when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);
assertThat(projectService.getById(projectId), is(expectedProject));
verify(projectRepository).findOne(projectId);

आप यह धारणा देते हैं कि आप निर्भरता भंडार का नकल करना चाहते हैं लेकिन आप भंडार पर प्रत्येक बुलाए गए विधि को भी देखना चाहते हैं।

सबसे पहले यह बेमानी है क्योंकि if यह दावा सत्य है: assertThat(projectService.getById(projectId), is(expectedProject));, इसका मतलब है कि भंडार को अपेक्षित तर्क के साथ बुलाया गया था और अपेक्षित परियोजना वापस कर दी है। इसे verify(projectRepository).findOne(projectId); से दोबारा क्यों जांचें?

दूसरे, एक इकाई परीक्षण का लक्ष्य यह जाँच नहीं कर रहा है कि निर्भरता पर प्रत्येक विधि को बुलाया गया था। यह दावे के संदर्भ में कई चीजें नहीं लाता है और आप निर्भरता के एपीआई के लिए इकाई परीक्षण को दृढ़ता से जोड़ते हैं और आप परीक्षण को और अधिक जटिल बनाते हैं।

0
davidxxx 20 जिंदा 2017, 20:48
धन्यवाद, मुझे आपका तर्क पसंद आया! मैंने उस असममित व्यवहार को उद्देश्य पर कोडित किया, यह इंगित करने के लिए कि हम पहले मामले में तर्क निर्दिष्ट नहीं करते हैं और बाद में कोड में सत्यापित करते हैं। तो जब भी आप कर सकते हैं आपको अपेक्षित मूल्यों को पास करना चाहिए? और दूसरे पैराग्राफ के बारे में: तो अगर मैं इस सेवा की निर्भरता को इस तरह से डिजाइन कर सकता हूं कि उनके तरीके केवल तर्कों को बदलने के बजाय कुछ मान लौटाते हैं (उदाहरण के लिए ProjectEntity ट्रांसफॉर्म (void transform(ProjectEntity entity) {...} के बजाय ProjectEntity entity) {...}), मुझे करना चाहिए कि परीक्षण डिजाइन में सुधार करने के लिए?
 – 
Roman Golyshev
21 जिंदा 2017, 01:24
आपका स्वागत है :) आपको भी धन्यवाद। आपके पहले प्रश्न के लिए: आपके मामले में आपको अपेक्षित मान पास करना चाहिए क्योंकि यह दावा करता है कि नकली को अपेक्षित तर्क के साथ बुलाया गया था। किसी के साथ मजाक करना और अंत में सत्यापन करना अंत में वही दावा करता है लेकिन यह बहुत अधिक क्रियात्मक है और जैसा कि कहा गया है कि आप निर्भरता के लिए अपना परीक्षण जोड़ते हैं। मान लीजिए कि आप नकली विधि में एक पैरामीटर जोड़ते हैं, तो आपको नए निर्भरता अनुबंध की पुष्टि करने के लिए अपनी परीक्षण पद्धति में दो बार बदलाव करना होगा। यह एक उपयुक्त समाधान नहीं है।
 – 
davidxxx
21 जिंदा 2017, 11:46
आपके दूसरे प्रश्न के लिए, मुझे लगता है कि मुझे "अपमानित समाधान" अभिव्यक्ति का उपयोग नहीं करना चाहिए था। इसका गलत अर्थ निकाला जा सकता है। जब मैं "अपमानित समाधान" का उल्लेख करता हूं तो मेरा मतलब है कि अगर निर्भरता पर एक विधि के लिए कुछ वापस करने का कोई मतलब नहीं है, तो केवल एक चीज जिसे मैं जांच सकता हूं वह यह है कि इसे अपेक्षित कहा जाता है। "मैं इस सेवा की निर्भरता को इस तरह से डिज़ाइन कर सकता हूं कि उनकी विधियां केवल तर्कों को बदलने के बजाय कुछ मान लौटाएं" यदि यह सेवा के एपीआई के लिए समझ में आता है, हां। अन्यथा, नहीं।
 – 
davidxxx
21 जिंदा 2017, 11:54
आपके विधि उदाहरण में, संदर्भ का उपयोग किए बिना उत्तर देना कठिन है। सामान्य तौर पर, मैं कहूंगा कि ProjectEntity को transform(ProjectEntity projectEntity) में वापस करना समझ में आता है यदि ProjectEntity अपरिवर्तनीय है या यदि आप एक धाराप्रवाह एपीआई चाहते हैं।
 – 
davidxxx
21 जिंदा 2017, 11:57
1
मेरा प्रश्न ऐसा करने के सर्वोत्तम (सबसे उचित) तरीके के बारे में है (तर्क की जांच करें)। मुझे ऐसा करने के तरीकों के बारे में पता है, लेकिन मैं अनुभवहीन हूं, इसलिए मैंने SO से पूछने का फैसला किया।
 – 
Roman Golyshev
21 जिंदा 2017, 21:10

सही कारण के लिए विफल और तेज़ होने के बाद UnitTest के बारे में तीसरी सबसे महत्वपूर्ण बात यह है कि पठनीय होना है।

परीक्षण के पाठक को आसानी से देखना चाहिए कि परीक्षण क्यों विफल हो रहा है। इसलिए अपने मॉक को यथासंभव सहिष्णु होने के लिए कॉन्फ़िगर करें और अपने सत्यापन में व्यवहार की जांच करें।

-1
Timothy Truckle 20 जिंदा 2017, 20:48
सिर्फ बदला लेने के लिए तर्क के बिना डाउनवोट के लिए धन्यवाद, यह बहुत वयस्क है ...
 – 
Timothy Truckle
22 जिंदा 2017, 16:28