2011-11-24 13 views
26

प्रभावी जावा इकाई परीक्षण एकमात्रसिंगलटन और इकाई परीक्षण

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

क्या कोई यह समझा सकता है कि ऐसा क्यों है?

+2

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

उत्तर

9

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

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

यह सभी वर्गों, सिंगलटन या नहीं के लिए सच है।

+1

अच्छी तरह से रखो! "मॉकिट" कंक्रीट कक्षाओं का नकल कर सकता है, लेकिन यह वास्तव में वास्तव में बुराई आत्मनिरीक्षण चीजें करता है जो बहुत अजीब व्यवहार कर सकता है। सिंगलेट्स (जावा में लागू के रूप में) बुराई है! Guice यह बेहतर करता है ... –

0

जहां तक ​​मुझे पता है, सिंगलटन को लागू करने वाली कक्षा को बढ़ाया नहीं जा सकता है (सुपरक्लस कन्स्ट्रक्टर को हमेशा स्पष्ट रूप से बुलाया जाता है और सिंगलटन में कन्स्ट्रक्टर निजी होता है)। यदि आप कक्षा को नकल करना चाहते हैं तो आपको कक्षा का विस्तार करना होगा। जैसा कि आप इस मामले में देखते हैं, यह संभव नहीं होगा।

+0

नहीं, यह पर्याप्त है कि कक्षा को मॉक करने के लिए एक इंटरफ़ेस लागू करता है, कुछ भी विस्तार करने की आवश्यकता नहीं है। पुस्तक से वाक्य में यह स्पष्ट रूप से भी कहा गया है। – ruhsuzbaykus

+0

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

7

मुझे लगता है कि यह वास्तव में सिंगलटन एक्सेस पैटर्न के कार्यान्वयन पर निर्भर करता है।

उदाहरण

MySingleton.getInstance() 

के लिए बहुत परीक्षण करने के लिए

MySingletonFactory mySingletonFactory = ... 
mySingletonFactory.getInstance() //this returns a MySingleton instance or even a subclass 

तथ्य यह है कि इसकी एक सिंगलटन का उपयोग कर के बारे में कोई जानकारी उपलब्ध नहीं करता है dificult हो सकता है। तो आप अपने कारखाने को स्वतंत्र रूप से बदल सकते हैं।

नोट: एक सिंगलटन, हालांकि जिस तरह से यह प्राप्त या संग्रहीत स्थिर साधनों के माध्यम से होना जरूरी नहीं है है एक आवेदन पत्र में उस वर्ग के केवल एक उदाहरण जा रहा द्वारा परिभाषित है।

+1

अच्छा बिंदु, लेकिन आजादी इस बात पर निर्भर करती है कि getInstance() विधि एक इंटरफ़ेस का हिस्सा है या नहीं। – duffymo

3

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

public enum Day { 
    MON(2), TUE(3), WED(4), THU(5), FRI(6), SAT(7), SUN(1); 

    private final int index; 

    private Day(int index) { 

    this.index = index; 
    } 

    public boolean isToday() { 

    return index == new GregorianCalendar().get(Calendar.DAY_OF_WEEK); 
    } 
} 

देखने चलो कहते हैं कि हम एक कोड है कि सप्ताहांत पर केवल निष्पादित किया जाना चाहिए करते हैं:

public void leisure() { 

    if (Day.SAT.isToday() || Day.SUN.isToday()) { 

    haveSomeFun(); 
    return; 
    } 

    doSomeWork(); 
} 

परीक्षण अवकाश विधि बहुत कठिन होने जा रहा है। इसका निष्पादन उस दिन निर्भर होगा जब इसे निष्पादित किया जाता है। यदि यह सप्ताहांत doSomeWork() पर निष्पादित किया जाएगा और सप्ताहांत haveSomeFun() पर लागू किया जाएगा।

इस मामले में हम, PowerMock जैसे कुछ भारी उपकरणों का उपयोग करने GregorianCalendar निर्माता रोकना एक नकली जो एक सूचकांक एक काम करने के दिन या दो परीक्षण मामलों leisure विधि के दोनों निष्पादन रास्तों के परीक्षण में सप्ताह के अंत में करने के लिए इसी वापस आ जाएगी लौटने की आवश्यकता होगी के लिए।

+0

एक हेवीवेट समाधान की तरह लगता है - आपके उदाहरण की सराहना करना केवल इतना है - हालांकि एक समय प्रदाता इंजेक्शन करने से अधिक सटीक परीक्षण की अनुमति मिल जाएगी और लागू करने के लिए बहुत जटिल हो जाएगा :-) – user924272

5

यह ओह इतना आसान है।

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

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

बिंदु यह है - आपको इस बात पर परवाह नहीं है कि आपके एसयूटी कक्षाएं कैसे काम पर निर्भर करती हैं। आप परवाह है कि आपका SUT उन वर्गों के साथ काम करता है। यही कारण है कि आप स्टब या नकली आपके एसयूटी की कक्षाओं की कक्षाएं हैं। और आप उन मॉक्स का उपयोग कर सकते हैं क्योंकि आप उन्हें अपने एसयूटी के लिए कन्स्ट्रक्टर पैरामीटर के रूप में पास कर सकते हैं।

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

समाधान डरपोक public static MySingleton getInstance() विधि का उपयोग करने के लिए नहीं है, लेकिन एक इंटरफेस पर निर्भर करने के लिए अपनी कक्षा के साथ काम करने के लिए की जरूरत है। ऐसा करें, और जब भी आपको आवश्यकता हो, आप परीक्षण युगल में पास कर सकते हैं।

+1

+1, सिंगलटन मेरे लिए एक विरोधी पैटर्न है, पिछले सदियों के अच्छे पुराने "वैश्विक चर" के समान। उचित ओओ और नकली-बीडीडी का अर्थ निर्भरता इंजेक्शन, और कोई सिंगलेट नहीं है। – Guillaume

+0

अगर मैं सही ढंग से यह उत्तर पढ़ता हूं, तो आप कह रहे हैं कि सहयोगी वर्गों का मज़ाक उड़ा रहा है ठीक है? यह दृष्टिकोण एक खनन क्षेत्र का एक छोटा सा हिस्सा है, और मैं * ज्यादातर * आपके साथ जवाब देता हूं .... हालांकि, सहयोगी वर्गों में परीक्षण करने के लिए कक्षाओं को एक अजीब स्थिति में डालकर सहयोगी वर्गों का मज़ाक उड़ाया नहीं जा रहा है जब इसके सहयोगियों का व्यवहार बदलता है? ऐसे मामले में, यूनिट टेस्ट एक सहयोगी के व्यवहार के बाद बदल जाएगा .... वास्तव में यह सुनिश्चित नहीं है कि चीजें काले और सफेद हैं, जैसा कि आप कहते हैं – user924272

+0

अच्छा विचार। यही कारण है कि यूनिट परीक्षण अकेले पर्याप्त नहीं है और आपको एकीकरण की आवश्यकता है और अंत में परीक्षण समाप्त हो सकता है। – user3657103

6

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

जब तक, आप (1) सिंगलटन को एक इंटरफ़ेस लागू नहीं करते हैं, और (2) उस इंटरफ़ेस का उपयोग करके सिंगलटन को अपनी कक्षा में इंजेक्ट करते हैं।

उदाहरण के लिए, एकमात्र आम तौर पर सीधे इस तरह instantiated कर रहे हैं:

public class MyClass 
{ 
    private MySingleton __s = MySingleton.getInstance() ; 

    ... 
} 

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

public class SomeSingleton 
    implements SomeInterface 
{ 
    ... 
} 

public class MyClass 
{ 
    private SomeInterface __s ; 

    public MyClass(SomeInterface s) 
    { 
     __s = s ; 
    } 

    ... 
} 

... 

MyClass m = new MyClass(SomeSingleton.getInstance()) ; 

:

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

Btw, इस तरह से यह करने के लिए नहीं है:

public class MyClass 
{ 
    private SomeInterface __s = SomeSingleton.getInstance() ; 

    public MyClass() 
    { 
    } 

    ... 
} 

कि अभी भी रन-टाइम में ही बाहर काम करता है, लेकिन परीक्षण के लिए अब आप फिर से SomeSingleton पर निर्भर हैं।

+0

इस तरह से वास्तव में अच्छा लगता है। इसलिए मैं किसी भी वर्ग के लिए स्वैप करने में सक्षम हूं क्योंकि इसके प्रकार "SomeInterface" है। लेकिन अब मैं सिंगलटन में उपयोग की जाने वाली सभी विधियों को कार्यान्वयन बनना सही है? उदाहरण के लिए यदि मैंने लॉग मैनेजर सिंगलटन बनाया है तो अब कुछ इंटरफेस में निम्नलिखित विधियां हैं - printLog, saveLog, setLogTag, resetLog, आदि इत्यादि। क्या मैं सही हूं कि इंटरफ़ेस में सभी सिंगलटन विधियों को अब घोषित किया जाना चाहिए? – j2emanue

3
it’s impossible to substitute a mock implementation for a singleton 

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

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

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

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

19

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

@Before 
public void resetSingleton() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 
    Field instance = MySingleton.class.getDeclaredField("instance"); 
    instance.setAccessible(true); 
    instance.set(null, null); 
} 

रेफरी: unit-testing-singletons

+0

अच्छा ... मेरी समस्या अब हल हो गई है। धन्यवाद :) –

संबंधित मुद्दे