2008-09-14 13 views
35

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

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

public class ClassWithStaticInit { 
    static { 
    System.out.println("static initializer."); 
    } 
} 

public class ClassWithStaticInit { 
    static { 
    staticInit(); 
    } 

    private static void staticInit() { 
    System.out.println("static initialized."); 
    } 
} 

करने के लिए बदल दिया जाएगा, ताकि हम एक JUnit में निम्न कर सकते हैं।

public class DependentClassTest { 
    public static class MockClassWithStaticInit { 
    public static void staticInit() { 
    } 
    } 

    @BeforeClass 
    public static void setUpBeforeClass() { 
    Mockit.redefineMethods(ClassWithStaticInit.class, MockClassWithStaticInit.class); 
    } 
} 

हालांकि यह समाधान भी अपनी समस्याओं के साथ आता है। आप उसी JVM पर DependentClassTest और ClassWithStaticInitTest नहीं चला सकते हैं क्योंकि आप वास्तव में स्थिर ब्लॉक को ClassWithStaticInitTest के लिए चलाने के लिए चाहते हैं।

इस कार्य को पूरा करने का आपका तरीका क्या होगा? या कोई बेहतर, गैर-जेमॉकिट आधारित समाधान जो आपको लगता है कि क्लीनर काम करेगा?

उत्तर

5

जब मैं इस समस्या में पड़, मैं आमतौर पर, एक ही बात आप का वर्णन करना छोड़कर मैं स्थिर विधि संरक्षित तो मैं इसे मैन्युअल रूप से आह्वान कर सकते हैं बनाते हैं। इसके शीर्ष पर, मैं सुनिश्चित करता हूं कि बिना किसी समस्या के विधि को कई बार बुलाया जा सकता है (अन्यथा यह स्थिर प्रारंभकर्ता से बेहतर नहीं है जहां तक ​​परीक्षण चलते हैं)।

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

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

4

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

मॉकिंग के लिए, मैं EasyMock का उपयोग करता हूं, लेकिन मैंने एक ही समस्या में भाग लिया है। विरासत कोड में स्थिर प्रारंभिक के दुष्प्रभाव परीक्षण को कठिन बनाते हैं। हमारा जवाब स्थैतिक प्रारंभकर्ता को दोबारा शुरू करना था।

+0

आप EasyMock के साथ 'स्थिर 'या' निजी 'विधियों का नकल नहीं कर सकते हैं। स्थैतिक प्रारंभकर्ता के शरीर को स्थानांतरित करना एक रिफैक्टरिंग तक है जो हम अभी कर सकते हैं। –

+0

यदि परीक्षण के तहत कक्षा में स्थिर init/निजी विधि है, तो आप इसे कॉल करना चाहते हैं। कोई बात नहीं। लेकिन अगर यह वर्ग मजाक कर रहा है, तो आसान मॉक के लिए कोई समस्या नहीं है: इसे कोई कॉल नहीं किया जाएगा क्योंकि कोई कार्यान्वयन नहीं है। आप सार्वजनिक इंटरफेस को सुरक्षित रूप से नकल कर सकते हैं जैसे कि निजी सामान मौजूद नहीं था। –

1

मुझे लगता है कि आप वास्तव में स्थिर प्रारंभकर्ता के बजाय किसी प्रकार का कारखाना चाहते हैं।

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

यह बताना मुश्किल है कि यह आपके कोड को देखे बिना संभव है या नहीं।

3

आप ग्रोवी में अपना टेस्ट कोड लिख सकते हैं और मेटाप्रोग्रामिंग का उपयोग करके स्थिर विधि को आसानी से नकल कर सकते हैं।

Math.metaClass.'static'.max = { int a, int b -> 
    a + b 
} 

Math.max 1, 2 

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

सधन्यवाद

+0

मुझे ग्रोवी पसंद है लेकिन दुर्भाग्य से हमारा सभी टेस्ट कोड जुनीट में होना चाहिए। –

+0

केम, लेकिन आप ग्रोवी में जूनिट (यहां तक ​​कि जूनिट 4) परीक्षण भी लिख सकते हैं। ;-) दयालु सम्मान – marcospereira

1

मैं नकली ढांचे में सुपर जानकार नहीं हूं इसलिए अगर मैं गलत हूं तो कृपया मुझे सही करें, लेकिन क्या आपके पास उल्लेख की जाने वाली स्थितियों को कवर करने के लिए संभवतः दो अलग-अलग नकली वस्तुएं नहीं हो सकतीं?

public static class MockClassWithEmptyStaticInit { 
    public static void staticInit() { 
    } 
} 

और

public static class MockClassWithStaticInit { 
    public static void staticInit() { 
    System.out.println("static initialized."); 
    } 
} 

के रूप में इस तरह के तो आप उन्हें अपने विभिन्न परीक्षण मामलों

@BeforeClass 
public static void setUpBeforeClass() { 
    Mockit.redefineMethods(ClassWithStaticInit.class, 
         MockClassWithEmptyStaticInit.class); 
} 

और

@BeforeClass 
public static void setUpBeforeClass() { 
    Mockit.redefineMethods(ClassWithStaticInit.class, 
         MockClassWithStaticInit.class); 
} 

क्रमशः में उपयोग कर सकते हैं।

+0

'System.out.println (" स्थिर प्रारंभिक। "); 'स्थिर प्रारंभकर्ता पहले स्थान पर क्या कर रहा है। हमें मजाक क्यों करना चाहिए कि यह वही चीज़ के साथ क्या कर रहा है? –

+0

कैम, मुझे लगता है कि आप मेरी बात याद कर चुके हैं। मैं चिंता का जवाब देने की कोशिश कर रहा हूं "हालांकि यह समाधान भी अपनी समस्याओं के साथ आता है। आप उसी जेवीएम पर निर्भर क्लासटेस्ट और क्लासविथस्टैटिक इंटटेस्ट नहीं चला सकते हैं क्योंकि आप वास्तव में क्लासिक विथस्टैटिक इंटटेस्ट के लिए स्थिर ब्लॉक को चलाने के लिए चाहते हैं।" – martinatime

+0

असल में, आपके पास क्लासविथस्टैटिकट के लिए दो अलग-अलग मॉक ऑब्जेक्ट्स हैं जो आपके निर्भर क्लासटेस्ट के साथ उपयोग के लिए हैं और दूसरा क्लासविथस्टैटिकइनटेस्ट के साथ उपयोग के लिए है। – martinatime

10

यह और अधिक "उन्नत" जेमॉकिट में जा रहा है। यह पता चला है, आप public void $clinit() विधि बनाकर JMockit में स्थिर प्रारंभिक ब्लॉक को फिर से परिभाषित कर सकते हैं। तो, बजाय इस परिवर्तन

public class ClassWithStaticInit { 
    static { 
    staticInit(); 
    } 

    private static void staticInit() { 
    System.out.println("static initialized."); 
    } 
} 

हम भी ClassWithStaticInit छोड़ सकता है के रूप में और कर MockClassWithStaticInit में निम्नलिखित बनाने की:

public static class MockClassWithStaticInit { 
    public void $clinit() { 
    } 
} 

वास्तव में हो जाएगा ताकि हमें में कोई परिवर्तन नहीं करने की अनुमति मौजूदा वर्ग

+0

पर मजाक नहीं करना चाहिए यह स्वीकार्य उत्तर होना चाहिए। एक अतिरिक्त के रूप में; @Mock एनोटेशन के साथ $ क्लिनिट के लिए सार्वजनिक एक्सेसर की आवश्यकता नहीं है। – user2219808

32

PowerMock एक और नकली ढांचा है जो EasyMock और Mockito को बढ़ाता है। PowerMock के साथ आप कक्षा से remove unwanted behavior आसानी से कर सकते हैं, उदाहरण के लिए एक स्थिर प्रारंभकर्ता। अपने उदाहरण में आप बस अपने JUnit परीक्षण मामले के लिए निम्न एनोटेशन जोड़ने:

@RunWith(PowerMockRunner.class) 
@SuppressStaticInitializationFor("some.package.ClassWithStaticInit") 

PowerMock एक जावा एजेंट का उपयोग नहीं है और इसलिए JVM स्टार्टअप मानकों के संशोधन की आवश्यकता नहीं है। आप जार फ़ाइल और उपर्युक्त एनोटेशन को सरल जोड़ते हैं।

9

कभी-कभी, मुझे कक्षाओं में स्थिर इनिटिलाइज़र मिलते हैं जो मेरा कोड इस पर निर्भर करता है।मैं कोड refactor नहीं कर सकते, मैं स्थिर प्रारंभकर्ता को दबाने के लिए PowerMock के @SuppressStaticInitializationFor एनोटेशन का उपयोग करें:

@RunWith(PowerMockRunner.class) 
@SuppressStaticInitializationFor("com.example.ClassWithStaticInit") 
public class ClassWithStaticInitTest { 

    ClassWithStaticInit tested; 

    @Before 
    public void setUp() { 
     tested = new ClassWithStaticInit(); 
    } 

    @Test 
    public void testSuppressStaticInitializer() { 
     asserNotNull(tested); 
    } 

    // more tests... 
} 

suppressing unwanted behaviour बारे में अधिक पढ़ें।

अस्वीकरण: पावरमैक एक ओपन सोर्स प्रोजेक्ट है जो मेरे दो सहयोगियों द्वारा विकसित किया गया है।

0

वास्तव में कोई जवाब नहीं है, लेकिन सिर्फ सोच रहा है - क्या Mockit.redefineMethods पर कॉल को "रिवर्स" करने का कोई तरीका नहीं है?
यदि कोई ऐसी स्पष्ट विधि मौजूद नहीं है, तो इसे अगले फैशन में फिर से निष्पादित नहीं करना चाहिए चाल चलाना चाहिए?

Mockit.redefineMethods(ClassWithStaticInit.class, ClassWithStaticInit.class); 

ऐसी पद्धति मौजूद है, तो आप इसे क्लास '@AfterClass विधि, और "मूल" स्थिर प्रारंभकर्ता ब्लॉक के साथ परीक्षण ClassWithStaticInitTest में, पर अमल कर सकता है जैसे कुछ भी नहीं बदला है, एक ही JVM से।

हालांकि यह सिर्फ एक झटका है, इसलिए मुझे कुछ याद आ रहा है।

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

  • कोई संबंधित समस्या नहीं^_^