परीक्षण क्षमता के लिए डिजाइन करते समय स्थिर उपयोगिता वर्गों के साथ कैसे व्यवहार करें


62

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

विभिन्न स्थानों पर हमारे लिए स्थिर सहायक विधियों जैसे कि ImageIO और URLEncoder (दोनों मानक जावा एपीआई) और विभिन्न अन्य पुस्तकालयों का उपयोग करना आवश्यक है, जिनमें ज्यादातर स्थैतिक विधियाँ होती हैं (जैसे अपाचे कॉमन्स पुस्तकालय)। लेकिन उन तरीकों का परीक्षण करना बेहद कठिन है जो ऐसे स्थिर सहायक वर्गों का उपयोग करते हैं।

इस समस्या को हल करने के लिए मेरे पास कई विचार हैं:

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

लेकिन मैं यह सोचता रहता हूं कि यह सिर्फ एक समस्या है जिसे टीडीडी करते समय बहुत से लोगों को सामना करना पड़ता है - इसलिए इस समस्या का समाधान पहले से ही होना चाहिए।

इन स्थिर सहायकों का परीक्षण करने वाले वर्गों को रखने के लिए सबसे अच्छी रणनीति क्या है?


मुझे यकीन नहीं है कि आपको "विश्वसनीय और / या आधिकारिक स्रोतों" से क्या मतलब है लेकिन मैं इस बात से सहमत हूं कि @berecursive ने अपने उत्तर में क्या लिखा है। पॉवरमॉक एक कारण से मौजूद है और ऐसा महसूस नहीं होना चाहिए "विशेष रूप से" अगर आप अपने आप को रैपर कक्षाएं नहीं लिखना चाहते हैं। इकाई परीक्षण (और TDD) की बात आने पर अंतिम और स्थैतिक विधियाँ एक दर्द है। व्यक्तिगत रूप से? मैं आपके द्वारा वर्णित विधि 2 का उपयोग करता हूं।
डेको

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

जवाबों:


34

(यहां कोई आधिकारिक "स्रोत नहीं है, मुझे डर है - ऐसा नहीं है कि अच्छी तरह से परीक्षण करने के लिए एक विनिर्देश है। बस मेरी राय, जो उम्मीद है कि उपयोगी होगी।)

जब ये स्थिर विधियां वास्तविक निर्भरता का प्रतिनिधित्व करती हैं, तो रैपर बनाएं। तो चीजों के लिए जैसे:

  • ImageIO
  • HTTP क्लाइंट (या कुछ और नेटवर्क से संबंधित)
  • फ़ाइल सिस्टम
  • वर्तमान समय प्राप्त करना (निर्भरता इंजेक्शन में मदद करने का मेरा पसंदीदा उदाहरण)

... यह एक इंटरफ़ेस बनाने के लिए समझ में आता है।

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

मुझे संदेह है कि सभी स्टैटिक कॉल्स को हटा दिया गया है जो वास्तव में पूर्वानुमान के साथ सुविधा के तरीके हैं , "शुद्ध" परिणाम (जैसे बेस 64 या URL एन्कोडिंग) तार्किक निर्भरता के एक पूरे बड़े गड़बड़ में प्रवेश बिंदुओं के बजाय (जैसे एचटीटीपी) आप इसे पूरी तरह से पाएंगे। वास्तविक निर्भरता के साथ सही काम करने के लिए व्यावहारिक।


20

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

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

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


1
एक अतिरिक्त मुद्दा जिसके बारे में मैं निश्चित रूप से आश्वस्त नहीं हूं: जब रैपर लागू करते हैं तो क्या आप उस वर्ग के सभी तरीकों को लागू करेंगे जो लिपटे हुए हैं या केवल उन हैं जो वर्तमान में आवश्यक हैं?

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

@AssafStone सहमत

पॉवरमॉक के साथ सावधान रहें, सभी तरीकों से छेड़छाड़ करने के लिए वर्ग हेरफेर करना पड़ता है जो बहुत अधिक उपरि के साथ आता है। यदि आप बड़े पैमाने पर इसका उपयोग करते हैं तो आपके परीक्षण बहुत धीमे होंगे।
bcarlso

क्या आपको वास्तव में बहुत रैपर लेखन करना है, अगर आपके परीक्षण / डीआई / आईओसी पुस्तकालय को अपनाने के साथ माइग्रेट करना है?

4

उन सभी के संदर्भ के रूप में जो इस समस्या से जूझ रहे हैं और इस सवाल पर आते हैं कि मैं यह वर्णन करने जा रहा हूं कि हमने समस्या से निपटने का फैसला कैसे किया:

हम मूल रूप से # 2 के रूप में उल्लिखित पथ का अनुसरण कर रहे हैं (स्थिर उपयोगिताओं के लिए रैपर कक्षाएं)। लेकिन हम केवल उनका उपयोग करते हैं जब वांछित आउटपुट का उत्पादन करने के लिए आवश्यक डेटा के साथ उपयोगिता प्रदान करना बहुत जटिल होता है (यानी जब हमें बिल्कुल विधि का मजाक उड़ाना पड़ता है)।

इसका मतलब है कि हमें अपाचे कॉमन्स की तरह एक साधारण उपयोगिता के लिए एक आवरण लिखना नहीं है StringEscapeUtils(क्योंकि उन्हें वे तार आसानी से उपलब्ध कराए जा सकते हैं) और हम स्थैतिक तरीकों के लिए मोज़ेक का उपयोग नहीं करते हैं (यदि हमें लगता है कि हमें इसे लिखने के लिए समय की आवश्यकता हो सकती है) एक रैपर क्लास और फिर रैपर का एक उदाहरण) नकली।


2

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


1

मैं एक प्रमुख बीमा कंपनी के लिए काम करता हूं और हमारा स्रोत कोड 400 एमबी शुद्ध जावा फाइलों तक जाता है। हम TDD के बारे में सोचे बिना पूरे एप्लिकेशन को विकसित कर रहे हैं। इस वर्ष से हमने प्रत्येक व्यक्तिगत घटक के लिए जूनिट परीक्षण के साथ शुरुआत की।

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

लेकिन यह वास्तव में तेज था और यह अब तक काफी अच्छा काम कर रहा है। मैं इसकी सलाह देता हूं - http://www.easymock.org/


1

ऑब्जेक्ट एक लक्ष्य को पूरा करने के लिए एक-दूसरे के साथ बातचीत करते हैं, जब आपके पास एनवायरमेंट (एक webservice एंडपॉइंट, डाओ लेयर डीबी तक पहुँचने के लिए, http रिक्वेस्ट पैरामीटर को हैंडल करने वाले कंट्रोलर) या आप अलगाव में अपनी ऑब्जेक्ट का परीक्षण करना चाहते हैं, तो परीक्षण करने के लिए कड़ी मेहनत करने वाली वस्तु है। आप उन वस्तुओं का मज़ाक उड़ाते हैं।

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


1

कभी-कभी मैं विकल्प 4 का उपयोग करता हूं

  1. रणनीति पैटर्न का उपयोग करें। स्थिर तरीकों के साथ एक उपयोगिता वर्ग बनाएं जो कार्यान्वयन को प्लग-इन करने योग्य इंटरफ़ेस के उदाहरण के लिए प्रस्तुत करता है। एक स्थिर इनिशियलाइज़र को कोड करें जो एक ठोस कार्यान्वयन में प्लग करता है। परीक्षण के लिए एक नकली कार्यान्वयन में प्लग करें।

कुछ इस तरह।

public class DateUtil {
    public interface ITimestampGenerator {
        long getUtcNow();
    }

    class ConcreteTimestampGenerator implements ITimestampGenerator {
        public long getUtcNow() { return System.currentTimeMillis(); }
    }

    private static ITimestampGenerator timestampGenerator;

    static {
        timestampGenerator = new ConcreteTimeStampGenerator;
    }

    public static DateTime utcNow() {
        return new DateTime(timestampGenerator.getUtcNow(), DateTimeZone.UTC);
    }

    public static void setTimestampGenerator(ITimestampGenerator t) {...}

    // plus other util routines, which may or may not use the timestamp generator 
}

इस दृष्टिकोण के बारे में मुझे जो पसंद है, वह यह है कि यह उपयोगिता के तरीकों को स्थिर रखता है, जो मुझे सही लगता है जब मैं पूरे कोड में कक्षा का उपयोग करने की कोशिश कर रहा हूं।

Math.sum(17, 29, 42);
// vs
new Math().sum(17, 29, 42);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.