किस आदेश के बाद जुनैत @ से पहले / @ बुलाए गए हैं?


133

मेरे पास इंटीग्रेशन टेस्ट सूट है। मेरे पास IntegrationTestBaseमेरे सभी परीक्षणों के विस्तार के लिए एक वर्ग है। इस बेस क्लास में @Before( public void setUp()) और है@Afterpublic void tearDown() एपीआई और डीबी कनेक्शन स्थापित करने के लिए ( ) विधि है। मैं जो कर रहा हूं, वह प्रत्येक टेस्टकेस और कॉलिंग में उन दो तरीकों को ओवरराइड कर रहा है super.setUp()और super.tearDown()। हालांकि यह समस्या पैदा कर सकता है अगर कोई सुपर को कॉल करना भूल जाता है या उन्हें गलत जगह पर रखता है और एक अपवाद फेंक दिया जाता है और वे अंत में या कुछ और में सुपर कॉल करना भूल जाते हैं।

मैं जो करना चाहता हूं वह आधार वर्ग पर setUpऔर tearDownविधियां बनाना है finalऔर फिर बस अपना एनोटेट @Beforeऔर जोड़ना है@After तरीकों को । कुछ प्रारंभिक परीक्षण करते हुए ऐसा प्रतीत होता है कि इस क्रम में हमेशा कॉल किया जाता है:

Base @Before
Test @Before
Test
Test @After
Base @After

लेकिन मैं बस थोड़ा चिंतित हूं कि आदेश की गारंटी नहीं है और यह समस्या पैदा कर सकता है। मैंने चारों ओर देखा और इस विषय पर कुछ भी नहीं देखा। क्या किसी को पता है कि क्या मैं ऐसा कर सकता हूं और कोई समस्या नहीं है?

कोड:

public class IntegrationTestBase {

    @Before
    public final void setUp() { *always called 1st?* }

    @After
    public final void tearDown() { *always called last?* }
}


public class MyTest extends IntegrationTestBase {

    @Before
    public final void before() { *always called 2nd?* }

    @Test
    public void test() { *always called 3rd?* }

    @After
    public final void after() { *always called 4th?* }
}

1
क्या MyTestलापता एक है extends?
एयरोब

@ वायो: अब और नहीं :)
जोएल

जवाबों:


135

हां, इस व्यवहार की गारंटी है:

@Before:

@Beforeजब तक कि वे वर्तमान कक्षा में अधिरोहित कर रहे हैं सुपर-क्लास के तरीकों, वर्तमान वर्ग के उन लोगों से पहले चलाया जाएगा। कोई अन्य आदेश निर्धारित नहीं है।

@After:

@Afterतरीकों सुपर-क्लास में घोषणा की, वर्तमान वर्ग के उन लोगों के बाद चलाया जाएगा जब तक कि वे वर्तमान कक्षा में अधिरोहित कर रहे हैं।


15
स्पष्ट होने के लिए, सभी @Beforeतरीकों के आदेश-निष्पादन की गारंटी नहीं है। यदि 10 @Beforeविधियां हैं, तो उनमें से प्रत्येक को किसी भी क्रम में निष्पादित किया जा सकता है; किसी अन्य विधि से ठीक पहले।
स्वाति

5
तो कुछ अस्पष्ट दस्तावेज उद्धृत करने के बजाय, क्या आप कृपया इसे अपने शब्दों में समझा सकते हैं? क्या @Beforeऔर @Afterविधियाँ हर दूसरे वर्ग विधि (एक बार विधि के अनुसार) से पहले चलती हैं , या कक्षा के तरीकों (एक बार प्रति कक्षा) के पूरे सूट के पहले और बाद में?
बीटी

5
जॉन क्यू सिटीजन द्वारा बताई गई महत्वपूर्ण पकड़ को देखें: "यह केवल तभी लागू होता है जब @Before के साथ चिह्नित प्रत्येक विधि वर्ग पदानुक्रम में एक अद्वितीय नाम है" याद रखने के लिए बहुत महत्वपूर्ण है!
ब्रूनो बोसोला

एक वर्ग में @Before (d) पद्धति और उसी सुपर क्लास में दूसरी विधि पर समान विधि नाम का उपयोग करके मेरा नाम संघर्ष था junit-4.12
स्टीफन

क्या यह नियम ConcordionRunner के @BeforeExample तरीकों पर भी लागू होता है?
एड्रियन प्रैंक

51

एक संभावित गचा जो मुझे पहले काट चुका है:

मुझे @Beforeप्रत्येक परीक्षण वर्ग में सबसे अधिक एक विधि पसंद है , क्योंकि @Beforeकक्षा के भीतर परिभाषित तरीकों को चलाने के आदेश की गारंटी नहीं है। आमतौर पर, मैं ऐसी विधि कहूंगाsetUpTest()

लेकिन, हालाँकि, @Beforeजैसा कि प्रलेखित किया गया है The @Before methods of superclasses will be run before those of the current class. No other ordering is defined., यह केवल तभी लागू होता है जब प्रत्येक विधि को चिह्नित किया जाता @Beforeहै जिसका वर्ग पदानुक्रम में एक अद्वितीय नाम होता है।

उदाहरण के लिए, मेरे पास निम्नलिखित थे:

public class AbstractFooTest {
  @Before
  public void setUpTest() { 
     ... 
  }
}

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() { 
    ...
  }
}

मुझे AbstractFooTest.setUpTest()पहले दौड़ने की उम्मीद थी FooTest.setUpTest(), लेकिन केवल FooTest.setupTest()निष्पादित किया गया था। AbstractFooTest.setUpTest()बुलाया नहीं गया था।

कोड को काम के रूप में संशोधित किया जाना चाहिए:

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() {
    super.setUpTest();
    ...
  }
}

सिर्फ आधार वर्ग में @Before विधि का नाम क्यों नहीं बदला? यह आपको सभी बच्चों में सुपर को कॉल करने से बचाएगा ... वैसे भी एक ही नाम के मुद्दे के साथ अच्छी पकड़
लॉरेंस टेरनी

24
चीजों को अधिक सुरक्षित बनाने के लिए बस एक टिप्पणी: नाम की गड़बड़ी से बचने के लिए आप बेस क्लास में @Before/ @Afterविधि (ओं) को बना सकते हैं final, इसलिए कंपाइलर शिकायत करेगा यदि आप (गलती से) उन्हें उपवर्ग में ओवरराइड करने की कोशिश करते हैं।
स्टीफन विंकलर

4
एक ही नाम की मूल विधि नहीं चल रही है जो JUnit व्यवहार की तरह नहीं है। यह लगता है कि ओओपी में बुनियादी ओवर-राइडिंग कैसे काम करती है। मूल विधि मूल रूप से रन टाइम पर मौजूद नहीं है। बच्चा इसे सभी इरादों और उद्देश्यों के लिए बदलता है। इसी तरह जावा काम करता है।
ब्रैंडन

1
एक अन्य गेटचा यह है कि मूल वर्गों को सार्वजनिक किया जाना चाहिए, अन्यथा @Beforeयदि उपवर्ग में भी एक @Beforeविधि है तो उनके चिह्नित तरीकों को अनदेखा किया जाएगा ।
जूल

21

मुझे लगता है कि दस्तावेजों के आधार पर @Beforeऔर @Afterसही निष्कर्ष तरीकों को अद्वितीय नाम देना है। मैं अपने परीक्षणों में निम्नलिखित पैटर्न का उपयोग करता हूं:

public abstract class AbstractBaseTest {

  @Before
  public final void baseSetUp() { // or any other meaningful name
    System.out.println("AbstractBaseTest.setUp");
  }

  @After
  public final void baseTearDown() { // or any other meaningful name
    System.out.println("AbstractBaseTest.tearDown");
  }
}

तथा

public class Test extends AbstractBaseTest {

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

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

  @Test
  public void test1() throws Exception {
    System.out.println("test1");
  }

  @Test
  public void test2() throws Exception {
    System.out.println("test2");
  }
}

परिणाम के रूप में दे

AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown

इस दृष्टिकोण का लाभ: AbstractBaseTest वर्ग के उपयोगकर्ता दुर्घटना से setUp / tearDown विधियों को ओवरराइड नहीं कर सकते। यदि वे चाहते हैं, तो उन्हें सटीक नाम पता होना चाहिए और वह कर सकते हैं।

(माइनर) इस दृष्टिकोण का नुकसान: उपयोगकर्ता यह नहीं देख सकते हैं कि उनके सेटअप / टियरडाउन से पहले या बाद में चीजें हो रही हैं। उन्हें यह जानने की जरूरत है कि ये चीजें अमूर्त वर्ग द्वारा प्रदान की गई हैं। लेकिन मुझे लगता है कि यही कारण है कि वे अमूर्त वर्ग का उपयोग करते हैं


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

मुझे लगता है कि यह ओपी के सर्वोत्तम उत्तर का आधार है, लेकिन आपको स्टैंडअलोन में अपने उत्तर को भरना चाहिए। क्या आप दूसरों को सुझाए गए विकल्पों को कवर करने के लिए अपने उदाहरण में वृद्धि कर सकते हैं, और बता सकते हैं कि आपका प्रस्ताव श्रेष्ठ क्यों है?
वच्र

2

यदि आप चीजों को घुमाते हैं, तो आप अपने बेस क्लास सार को घोषित कर सकते हैं, और वंशज के पास सेटअप और टियरडाउन तरीके (एनोटेशन के बिना) घोषित होते हैं, जिन्हें बेस क्लास के एनोटेट सेटअप और टियरडाउन तरीकों से बुलाया जाता है।


1
एक बुरा विचार नहीं है, लेकिन मैं उन परीक्षणों पर एक अनुबंध लागू नहीं करना चाहता हूं जिन्हें अपने स्वयं के सेटअप / आंसू की जरूरत नहीं है
जोएल

2

आप @BeforeClassएनोटेशन का उपयोग यह आश्वस्त करने के लिए कर सकते हैं कि setup()हमेशा पहले कहा जाता है। इसी तरह, आप @AfterClassएनोटेशन का उपयोग यह आश्वासन देने के लिए कर सकते हैं कि tearDown()हमेशा अंतिम कहा जाता है।

यह आमतौर पर अनुशंसित नहीं है, लेकिन यह समर्थित है

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


2
वास्तव में, यदि आप ऐसा करने के लिए थे, मैं एक विधि बनाने की सलाह देते हैं setupDB()और closeDB()और उनके साथ अंकन @BeforeClassऔर @AfterClassऔर साथ तरीकों के बाद अपने पहले / जगह setup()औरtearDown()
स्वाती

तरीकों के साथ व्याख्या की @BeforeClassऔर @AfterClassस्थिर होने की जरूरत है। जब हम इन तरीकों के अंदर उदाहरण चर का उपयोग करना चाहते हैं, तो मामले के बारे में क्या?
प्रतीक सिंघल

@BeforeClassपावरमॉक के साथ उपयोग करते समय एक चेतावनी : यह केवल पहले टेस्ट रन के लिए काम करता है। इस मुद्दे को देखें: github.com/powermock/powermock/issues/398
Dagmar

2

यह टैगलाइन प्रश्न का उत्तर नहीं है, लेकिन यह प्रश्न के शरीर में वर्णित समस्याओं का उत्तर है। @Before या @After का उपयोग करने के बजाय, @ org.junit.Rule का उपयोग करना देखें क्योंकि यह आपको अधिक लचीलापन देता है। ExternalResource (4.7 के रूप में) वह नियम है जिसमें आप सबसे अधिक रुचि रखते हैं यदि आप कनेक्शन का प्रबंधन कर रहे हैं। इसके अलावा, यदि आप चाहते हैं कि आपके नियमों का निष्पादन आदेश रुलचैन (4.10 तक) का उपयोग करें। मेरा मानना ​​है कि ये सभी उपलब्ध थे जब यह सवाल पूछा गया था। नीचे दिए गए कोड का उदाहरण बाहरी स्रोत javadocs से कॉपी किया गया है।

 public static class UsesExternalResource {
  Server myServer= new Server();

  @Rule
  public ExternalResource resource= new ExternalResource() {
      @Override
      protected void before() throws Throwable {
          myServer.connect();
         };

      @Override
      protected void after() {
          myServer.disconnect();
         };
     };

  @Test
  public void testFoo() {
      new Client().run(myServer);
     }
 }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.