2014-12-09 5 views
13

वर्तमान में, मेरे सभी जुनीट परीक्षण एक सामान्य बेस क्लास से विस्तारित होते हैं जो @BeforeClass और @AfterClass एनोटेशन के साथ टैग की गई विधियां प्रदान करता है - ये सभी वास्तव में स्थिर संसाधनों/सेवाओं का एक समूह स्थापित करते हैं उपयोग करने के लिए परीक्षण।एकाधिक परीक्षण कक्षाओं के बीच JUnit FirstClass तर्क को कैसे साझा करें

यह कुछ कारणों के लिए मेरे लिए एक अजीब लगता है:

  1. JUnit4 के बिंदु (मेरी समझ से) का एक हिस्सा है कि हम इस क्लासिकल परीक्षण विरासत की कोई आवश्यकता नहीं करना चाहिए।
  2. जब मैं व्यक्तिगत रूप से (हम अक्सर करते हैं), @BeforeClass और @AfterClass कई बार अनुरोध किया गया हो, नीचे परीक्षण धीमा सुइट का एक हिस्सा के बजाय के रूप में इन परीक्षण चलाने - हम वास्तव में केवल इन एक बार
बुला जाना चाहिए

मैं जो करना चाहता हूं वह किसी भी तरह से मौजूदा सेक्लास/आफ्टरक्लास लॉजिक को विरासत श्रृंखला से बाहर ले जाता है और किसी ऐसे व्यक्ति में जिसे व्यक्तिगत परीक्षण और सूट द्वारा साझा किया जा सकता है।

क्या यह किया जा सकता है? यदि हां, तो कैसे? (यदि यह मायने रखती है, मैं JUnit 4.7 का उपयोग कर रहा है, और यह एक अलग संस्करण के लिए अद्यतन करने के लिए एक कठिन बेचने हो सकता है)

उत्तर

21

प्रथम अंक के लिए एक समाधान एक @ClassRule, JUnit 4.9 में शुरू की के माध्यम से org.junit.rules.ExternalResource परीक्षण को झुका का ही विस्तार में तर्क स्थानांतरित करने के लिए है:

public class MyTest { 
    @ClassRule 
    public static final TestResources res = new TestResources(); 
    @Test 
    public void testFoo() { 
     // test logic here 
    } 
} 

public class TestResources extends ExternalResource { 
    protected void before() { 
     // Setup logic that used to be in @BeforeClass 
    } 
    protected void after() { 
     // Setup logic that used to be in @AfterClass 
    } 
} 

इस तरह, संसाधनों पहले बेस क्लास द्वारा प्रबंधित किया जाता है, टेस्ट क्लास पदानुक्रम से बाहर और अधिक मॉड्यूलर/उपभोग्य "संसाधन" में स्थानांतरित किया जाता है जिसे क्लास रन के बाद कक्षा चलाने और नष्ट करने से पहले बनाया जा सकता है।

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

बस एक कारखाना पैटर्न है कि संदर्भ आंतरिक रूप से गिनती निर्धारित करने के लिए किया जाए या नहीं/बनाने के लिए संसाधन को नष्ट करता है में @ClassRule संसाधन सृजन बदल जाते हैं।

उदाहरण के लिए (कृपया ध्यान दें यह किसी न किसी तरह है और मजबूती के लिए से निपटने के कुछ तोड़ मरोड़/त्रुटि आवश्यकता हो सकती है):

public class TestResources extends ExternalResource { 
    private static int refCount = 0; 

    private static TestResources currentInstance; 

    public static TestResources getTestResources() { 
     if (refCount == 0) { 
      // currentInstance either hasn't been created yet, or after was called on it - create a new one 
      currentInstance = new TestResources(); 
     } 
     return currentInstance; 
    } 

    private TestResources() { 
     System.out.println("TestResources construction"); 
     // setup any instance vars 
    } 

    protected void before() { 
     System.out.println("TestResources before"); 
     try { 
      if (refCount == 0) { 
       System.out.println("Do actual TestResources init"); 
      } 
     } 
     finally { 
      refCount++; 
     } 
    } 

    protected void after() { 
     System.out.println("TestResources after"); 
     refCount--; 
     if (refCount == 0) { 
      System.out.println("Do actual TestResources destroy"); 
     } 
    } 
} 

दोनों अपने सूट/परीक्षण कक्षाएं सिर्फ कारखाने विधि के माध्यम से एक @ClassResource के रूप में संसाधन का प्रयोग करेंगे:

@RunWith(Suite.class) 
@SuiteClasses({FooTest.class, BarTest.class}) 
public class MySuite { 
    @ClassRule 
    public static TestResources res = TestResources.getTestResources(); 
    @BeforeClass 
    public static void suiteSetup() { 
     System.out.println("Suite setup"); 
    } 
    @AfterClass 
    public static void suiteTeardown() { 
     System.out.println("Suite teardown"); 
    } 
} 
public class FooTest { 
    @ClassRule 
    public static TestResources res = TestResources.getTestResources(); 

    @Test 
    public void testFoo() { 
     System.out.println("testFoo"); 
    } 
} 
public class BarTest { 
    @ClassRule 
    public static TestResources res = TestResources.getTestResources(); 

    @Test 
    public void testBar() { 
     System.out.println("testBar"); 
    } 
} 

जब एक व्यक्ति परीक्षण चलाने के refcounting कोई असर नहीं होगा - "वास्तविक init" और "वास्तविक टियरडाउन" केवल एक बार नहीं होगा। सूट के माध्यम से चलते समय, सूट टेस्ट रिसोर्स बनायेगा, और व्यक्तिगत परीक्षण पहले से ही तत्काल तत्काल पुन: उपयोग करेंगे (refcounting इसे वास्तव में नष्ट होने और सूट में परीक्षणों के बीच पुनर्निर्मित होने से रोकता है)।

+0

मुझे लगता है कि आपकी रेफकाउंट सिस्टम काम नहीं करती है। यह प्रत्येक वर्ग के बाद शून्य हो जाएगा और आपके संसाधन को हटा देगा और फिर getTestResources() को कॉल करते समय इसे अगले वर्ग पर पुन: प्रारंभ करें। मुझे कुछ याद आ रहा है, अगर मैं गलत हूं तो मुझे सही करें। – FredBoutin

+1

विचार यह है कि यह एक व्यक्तिगत परीक्षण या परीक्षण के एक सूट चलाने के दौरान शुरू कर सकते हैं। ** सूट ** के रूप में चलते समय, रीफकाउंट सूट 'क्लासरुले' द्वारा 1 पर सेट किया जाता है। प्रत्येक परीक्षण refCount 2 को बदल देगा, फिर इसे पूरा होने पर 1 तक वापस कर देगा। जब सुइट किया जाता है, तो यह 0 पर जाएगा। ** ** परीक्षण के रूप में चलते समय, रेफकाउंट व्यक्तिगत परीक्षण द्वारा 1 पर सेट किया जाता है, फिर परीक्षण पूरा होने पर 0 तक। मुद्दा यह है कि संसाधन केवल चलाने के दोनों तरीकों में एक बार बनाया और नष्ट हो जाता है। – Krease

+0

बहुत स्पष्ट धन्यवाद @ क्रेज़! – FredBoutin

1

आप SUITE कक्षा में @BeforeClass और @AfterClassउपयोग कर सकते हैं।

इस सुइट में अमल में परीक्षण वर्गों में से किसी से पहले तरीकों चलेंगे और सभी परीक्षण वर्गों (क्रमशः) खत्म करने के बाद

इस तरह आप उन्हें केवल एक बार चला सकते हैं।

//..usual @RunWith etc annotations here 
public class MySuite{ 

@BeforeClass 
public static void setup(){ 

} 

@AfterClass 
public static void tearDown(){ 

} 

} 
+1

पर

@RunWith(JUnitSharedResourceRunner.class) @JUnitSharedResourceRunner.WithSharedResources({SampleSharedResource.class}) public class SharedResourceRunnerATest { ... 

सूत्रों का कहना है हालांकि यह अच्छा होगा यदि मैं केवल कभी एक सूट वर्ग से परीक्षण चलाने के है, यह जब मैं परीक्षण व्यक्तिगत रूप से चलाने के काम नहीं करेगा (यानी : ग्रहण में, परीक्षण पर राइट क्लिक करें और "जुनीट टेस्ट के रूप में चलाएं" चुनें) - सूट परीक्षणों के बारे में जानता है, लेकिन परीक्षण सूट के बारे में नहीं जानते ... – Krease

0

मुझे इसी तरह की समस्या मिली (वसंत एक विकल्प नहीं था और मैं मैवेन प्रोजेक्ट्स में टेस्टसुइट्स नहीं लिखता) इसलिए मैंने इस समस्या को हल करने के लिए सरल जूनिट धावक लिखा।

आपको साझा संसाधन स्रोत लिखने और उस संसाधन की आवश्यकता के लिए अपने परीक्षण को चिह्नित करने की आवश्यकता है।

public class SampleSharedResource implements SharedResource { 
public void initialize() throws Exception { 
    //init your resource 
} 

} https://github.com/eanlr/junit-shared-resources-runner

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