unit testing मैं निजी फ़ंक्शन या क्लास का परीक्षण कैसे करूं जिसमें निजी विधियां, फ़ील्ड या आंतरिक कक्षाएं हों?




unit-testing tdd (20)

मैं यूनिट टेस्ट (xUnit का उपयोग करके) एक कक्षा जिसमें आंतरिक निजी विधियों, फ़ील्ड या नेस्टेड कक्षाएं हैं? या एक ऐसा कार्य जिसे आंतरिक संबंध (सी / सी ++ में static ) द्वारा निजी बनाया गया है या एक निजी ( anonymous ) नामस्थान में है?

परीक्षण चलाने में सक्षम होने के लिए किसी विधि या फ़ंक्शन के लिए एक्सेस संशोधक को बदलने में बुरा लगता है।

https://code.i-harness.com


आम तौर पर एक इकाई परीक्षण का उद्देश्य कक्षा या इकाई के सार्वजनिक इंटरफ़ेस का उपयोग करना है। इसलिए, निजी विधियां कार्यान्वयन विस्तार हैं जिन्हें आप स्पष्ट रूप से परीक्षण करने की उम्मीद नहीं करेंगे।


इस लेख से: जुनीट और सुइटरनर (बिल वेनेर्स) के साथ निजी तरीके का परीक्षण , आपके मूल रूप से 4 विकल्प हैं:

  • निजी तरीकों का परीक्षण न करें।
  • विधियों के पैकेज की अनुमति दें।
  • एक नेस्टेड टेस्ट क्लास का प्रयोग करें।
  • प्रतिबिंब का प्रयोग करें।

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

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

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


जब मेरे पास ऐसी कक्षा में निजी तरीके हैं जो पर्याप्त जटिल हैं कि मुझे सीधे निजी तरीकों का परीक्षण करने की आवश्यकता महसूस होती है, तो यह एक कोड गंध है: मेरी कक्षा बहुत जटिल है।

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

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

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


जैसा कि अन्य ने कहा है ... सीधे निजी तरीकों का परीक्षण न करें। यहां कुछ विचार दिए गए हैं:

  1. सभी विधियों को छोटे और केंद्रित रखें (परीक्षण करने में आसान, गलत क्या ढूंढना आसान है)
  2. कोड कवरेज उपकरण का प्रयोग करें। मुझे Cobertura पसंद है (ओह शुभ दिन, ऐसा लगता है कि एक नया संस्करण बाहर है!)

यूनिट परीक्षणों पर कोड कवरेज चलाएं। यदि आप देखते हैं कि विधियों को पूरी तरह से परीक्षण नहीं किया गया है तो कवरेज प्राप्त करने के लिए परीक्षणों में जोड़ें। 100% कोड कवरेज के लिए लक्ष्य रखें, लेकिन महसूस करें कि आपको शायद यह नहीं मिलेगा।


निजी तरीकों का परीक्षण करना आपकी कक्षा के encapsulation को तोड़ता है क्योंकि हर बार जब आप आंतरिक कार्यान्वयन बदलते हैं तो आप क्लाइंट कोड तोड़ते हैं (इस मामले में, परीक्षण)।

तो निजी तरीकों का परीक्षण न करें।


बड़ी और quirky कक्षाओं के साथ विरासत कोड का परीक्षण करने के लिए, यह अक्सर एक निजी (या सार्वजनिक) विधि का परीक्षण करने में सक्षम होने के लिए बहुत मददगार है जो मैं अभी लिख रहा हूं।

मैं जावा के लिए junitx.util.PrivateAccessor -package का उपयोग करता हूं । निजी विधियों और निजी क्षेत्रों तक पहुंचने के लिए बहुत से उपयोगी एक-लाइनर।

import junitx.util.PrivateAccessor;

PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);

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


मैंने एक अन्य दृष्टिकोण का उपयोग निजी या संरक्षित पैकेज के लिए एक निजी विधि को बदलना है, फिर इसे Google Guava लाइब्रेरी के @VisibleForTesting एनोटेशन के साथ पूरक बनाएं

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

उदाहरण के लिए, यदि परीक्षण करने की विधि src/main/java/mypackage/MyClass.java src/test/java/mypackage/MyClassTest.java तो आपका परीक्षण कॉल src/test/java/mypackage/MyClassTest.java में रखा जाना चाहिए। इस तरह, आपको अपनी टेस्ट क्लास में टेस्ट विधि तक पहुंच मिली है।


मैंने जावा के लिए अतीत में ऐसा करने के लिए reflection का उपयोग किया है, और मेरी राय में यह एक बड़ी गलती थी।

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

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


यदि आप मौजूदा कोड का परीक्षण करने की कोशिश कर रहे हैं कि आप अनिच्छुक हैं या बदलने में असमर्थ हैं, तो प्रतिबिंब एक अच्छी पसंद है।

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

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


यदि आप विरासत एप्लिकेशन के निजी तरीकों का परीक्षण करना चाहते हैं जहां आप कोड नहीं बदल सकते हैं, तो जावा के लिए एक विकल्प jMockit , जो आपको क्लास के लिए निजी होने पर भी किसी ऑब्जेक्ट पर jMockit बनाने की अनुमति देगा।


यदि स्प्रिंग का उपयोग करते हैं, तो ReflectionTestUtils कुछ आसान टूल प्रदान करता है जो न्यूनतम प्रयास के साथ यहां सहायता करते हैं। उदाहरण के लिए, एक अवांछित सार्वजनिक सेटर जोड़ने के लिए मजबूर किए बिना किसी निजी सदस्य पर नकली सेट अप करने के लिए:

ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);

सार्वजनिक तरीकों से निजी तरीकों का उपभोग किया जाता है। अन्यथा, वे मृत कोड हैं। यही कारण है कि आप सार्वजनिक विधि का अनुमान लगाते हैं, सार्वजनिक पद्धति के अपेक्षित परिणामों पर जोर देते हैं और इस प्रकार, निजी तरीकों से इसका उपभोग होता है।

सार्वजनिक तरीकों पर अपने यूनिट परीक्षण चलाने से पहले निजी तरीकों का परीक्षण डीबगिंग द्वारा किया जाना चाहिए।

परीक्षण-संचालित विकास का उपयोग करके उन्हें डीबग किया जा सकता है, जब तक आपके सभी दावों को पूरा नहीं किया जाता है, तब तक आपके यूनिट परीक्षणों को डीबग कर सकते हैं।

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


स्प्रिंग फ्रेमवर्क में आप इस विधि का उपयोग करके निजी विधियों का परीक्षण कर सकते हैं:

ReflectionTestUtils.invokeMethod()

उदाहरण के लिए:

ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");

JUnit.org FAQ पृष्ठ का उत्तर:

लेकिन अगर आपको चाहिए ...

यदि आप जेडीके 1.3 या उच्चतर का उपयोग कर रहे हैं, तो आप PrivilegedAccessor की सहायता से एक्सेस कंट्रोल मैकेनिज्म को हटाने के लिए प्रतिबिंब का उपयोग कर सकते हैं। इसका उपयोग कैसे करें इसके विवरण के लिए, इस आलेख को पढ़ें।

यदि आप जेडीके 1.6 या उच्चतर का उपयोग कर रहे हैं और आप अपने परीक्षण @Test के साथ एनोटेट करते हैं, तो आप अपने परीक्षण विधियों में प्रतिबिंब इंजेक्ट करने के लिए डीपी 4j का उपयोग कर सकते हैं। इसका उपयोग कैसे करें इसके विवरण के लिए, इस टेस्ट स्क्रिप्ट को देखें।

पीएस मैं डीपी 4 जे में मुख्य योगदानकर्ता हूं , मुझसे पूछें कि आपको मदद चाहिए या नहीं। :)


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

यदि आप ऐसा करते हैं, तो यह देखने के लिए एक कोड कवरेज टूल (एम्मा की तरह) का उपयोग करना एक अच्छा विचार है कि वास्तव में आपके निजी तरीकों को आपके परीक्षणों से निष्पादित किया जा रहा है या नहीं।


निजी क्षेत्रों का परीक्षण करने के लिए मेरा सामान्य कार्य यहां दिया गया है:

protected <F> F getPrivateField(String fieldName, Object obj)
    throws NoSuchFieldException, IllegalAccessException {
    Field field =
        obj.getClass().getDeclaredField(fieldName);

    field.setAccessible(true);
    return (F)field.get(obj);
}

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

आप मालिक वर्ग के बाहर से निजी तरीकों को प्राप्त करने के लिए प्रतिबिंब का उपयोग नहीं कर सकते हैं, निजी संशोधक प्रतिबिंब को भी प्रभावित करता है

यह सच नहीं है। आप निश्चित रूप से कर सकते हैं, जैसा कि केम Catikkas के जवाब में उल्लेख किया गया है ।


मुझे हाल ही में यह समस्या थी और Picklock नामक एक छोटा सा टूल लिखा , जो जावा प्रतिबिंब एपीआई का स्पष्ट रूप से उपयोग करने की समस्याओं से बचाता है, दो उदाहरण:

कॉलिंग विधियों, उदाहरण के लिए private void method(String s)- जावा प्रतिबिंब द्वारा

Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");

कॉलिंग विधियों, उदाहरण के लिए private void method(String s)- पिकलॉक द्वारा

interface Accessible {
  void method(String s);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");

फ़ील्ड सेट करना, उदाहरण के लिए private BigInteger amount;- जावा प्रतिबिंब द्वारा

Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));

फ़ील्ड सेट करना, उदाहरण के लिए private BigInteger amount;- पिकलॉक द्वारा

interface Accessible {
  void setAmount(BigInteger amount);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));




tdd