2013-10-10 2 views
10

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

तो दूसरे शब्दों में कुछ ऐसा करने का तरीका:

Foo.setBar("Hello"); 

// Somehow reinitialize Foo 

String bar = Foo.getBar(); // Gets default value of bar rather than "Hello" 

दुर्भाग्य से, मैं फू को बदल नहीं सकते, इसलिए मैं इसे प्रयोग अटक कर रहा हूँ।

संपादित करें ऐसा प्रतीत होता है कि मैंने अपना उदाहरण थोड़ा सा सरल बना दिया। वास्तविक कोड "बार" में सिस्टम प्रॉपर्टी द्वारा सेट किया जाता है और एक आंतरिक स्थिर चर पर सेट हो जाता है। तो एक बार यह दौड़ना शुरू हो जाने पर, मैं इसे बदल नहीं सकता।

+0

यह स्पष्ट नहीं है कि तुम क्या कह रहे हैं। क्या आप एक विशिष्ट समय (कब?) पर कोड चलाने के लिए एक जुनीट सुविधा की तलाश में हैं, या आप पूछ रहे हैं कि बाहरी शुरू होने के बाद बाहरी 'फू' कक्षा को संशोधित करना संभव है या नहीं? – chrylis

+0

आप निश्चित रूप से फू को संशोधित कर सकते हैं ताकि यह उत्परिवर्तनीय हो, लेकिन आप स्पष्ट रूप से उस से निषिद्ध हैं। क्लास को फिर से लोड करने की अनुमति देने के लिए निजी वर्ग लोडर और प्रतिबिंबों का उपयोग करने का एकमात्र अन्य विकल्प है। लेकिन 'Foo.getBar()' कॉल को फिर से काम करना होगा। –

+1

[जावा: कैसे एक स्थैतिक वर्ग को "पुनरारंभ करें" के संभावित डुप्लिकेट?] (Http://stackoverflow.com/questions/4631565/java-how-to-restart-a-static-class) –

उत्तर

4

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

final Field field = clazz.getDeclaredField(fieldName); 
field.setAccessible(true); 
final Field modifiersField = Field.class.getDeclaredField("modifiers"); 
modifiersField.setAccessible(true); 
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 

field.set(null, value); 
5

यदि आप पावरमैक का उपयोग करते हैं, तो आप स्थिर तरीकों का नकल कर सकते हैं - जो आपको करना चाहिए।

2

आप पावरमॉक (मॉकिटो के साथ) या जेमॉकिट का उपयोग स्थिर वर्ग को नकल करने के लिए कर सकते हैं ताकि आप प्रत्येक टेस्ट में जो भी चाहें कर सकें।

2

तीन सुझाव,

  1. कॉल @Before से स्थिर विधि कुछ ज्ञात मान में स्थापित कर लिया।

  2. प्रतिबिंब के माध्यम से मूल्य निर्धारित करने के लिए ReflectionTestUtils का उपयोग करें।

  3. एक उदाहरण रैपर वर्ग रखने के लिए अपना कोड अपडेट करें जो एक आवृत्ति विधि/वर्ग में स्थिर विधि को कॉल को लपेटता है। रैपर को नकली करें और परीक्षण के तहत अपनी कक्षा में इंजेक्ट करें।

+0

(1) केवल तभी काम करता है जब कोई अन्य परीक्षण मूल्य संशोधित नहीं करता है - यानी यह नाजुक है। – ash

0

मैं init और destroy स्थिर तरीकों कि सभी उदाहरणों के बारे में ध्यान रखना चाहिए साथ Factory पैटर्न का प्रयोग करेंगे।

कुछ की तरह:

public class FooFactory { 

    private static Foo mFoo = null; 

    public static Foo init(){ 

     if(mFoo == null){ 
      mFoo = new Foo(); 
     } 
     return mFoo; 
    } 

    public static void destroy(){ 
     if(mFoo != null){ 
      mFoo = null; 
     } 
    } 
} 

तो प्रति UniTest चलाने के लिए पर्याप्त:

FooFactory.init();// on start 

.... 

FooFactory.destroy();// on finish 
0

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

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

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

0

यह एक छोटा सा उदाहरण है, जहां स्थिर प्रारंभकर्ता का उपयोग कर एक उपयोगिता वर्ग कि उपयोगिता की कसौटी पर प्रारंभ फिर से लोड किया जाता है। उपयोगिता एक स्थिर अंतिम मूल्य आरंभ करने के लिए सिस्टम प्रॉपर्टी का उपयोग करती है। आमतौर पर रनटाइम पर यह मान बदला नहीं जा सकता है। तो JUnit परीक्षण पुन: लोड वर्ग स्थिर प्रारंभकर्ता चलाने फिर से करने के लिए ...

उपयोगिता:

public class Util { 
    private static final String VALUE; 

    static { 
     String value = System.getProperty("value"); 

     if (value != null) { 
      VALUE = value; 
     } else { 
      VALUE = "default"; 
     } 
    } 

    public static String getValue() { 
     return VALUE; 
    } 
} 

JUnit परीक्षण:

import static org.junit.Assert.assertEquals; 

import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 

import org.junit.Test; 

public class UtilTest { 

    private class MyClassLoader extends ClassLoader { 

     public Class<?> load() throws IOException { 
      InputStream is = MyClassLoader.class.getResourceAsStream("/Util.class"); 

      ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
      int b = -1; 

      while ((b = is.read()) > -1) { 
       baos.write(b); 
      } 

      return super.defineClass("Util", baos.toByteArray(), 0, baos.size()); 
     } 
    } 

    @Test 
    public void testGetValue() { 
     assertEquals("default", getValue()); 
     System.setProperty("value", "abc"); 
     assertEquals("abc", getValue()); 
    } 

    private String getValue() { 
     try { 
      MyClassLoader myClassLoader = new MyClassLoader(); 
      Class<?> clazz = myClassLoader.load(); 
      Method method = clazz.getMethod("getValue"); 
      Object result = method.invoke(clazz); 
      return (String) result; 
     } catch (IOException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { 
      throw new IllegalStateException("Error at 'getValue': " + e.getLocalizedMessage(), e); 
     } 
    } 
} 
संबंधित मुद्दे

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