Java: System.exit () को कॉल करने वाली विधियों का परीक्षण कैसे करें?


199

मुझे कुछ तरीके मिले हैं जिन्हें System.exit()कुछ इनपुट्स पर कॉल करना चाहिए । दुर्भाग्य से, इन मामलों का परीक्षण करने से JUnit समाप्त हो जाता है! System.exit()जेवीएम को समाप्त करने के लिए, न केवल मौजूदा थ्रेड को समाप्त करने के लिए विधि कॉल को एक नए थ्रेड में रखने से मदद नहीं लगती है । क्या इससे निपटने के लिए कोई सामान्य पैटर्न हैं? उदाहरण के लिए, क्या मैं एक ठूंठ को निर्वाह कर सकता हूं System.exit()?

[संपादित करें] प्रश्न में वर्ग वास्तव में एक कमांड-लाइन टूल है जिसे मैं JUnit के अंदर परीक्षण करने का प्रयास कर रहा हूं। शायद JUnit नौकरी के लिए सही उपकरण नहीं है? पूरक प्रतिगमन परीक्षण उपकरणों के सुझाव का स्वागत है (अधिमानतः कुछ जो कि JUnit और EclEmma के साथ अच्छी तरह से एकीकृत होता है)।


2
मैं इस बात से उत्सुक हूं कि कोई फ़ंक्शन कभी System.exit () को कॉल क्यों करेगा ...
थॉमस ओवंस

2
यदि आप एक फ़ंक्शन को कॉल कर रहे हैं जो एप्लिकेशन से बाहर निकलता है। उदाहरण के लिए, यदि उपयोगकर्ता किसी कार्य को करने की कोशिश करता है तो वे पंक्ति में x बार अधिक प्रदर्शन करने के लिए अधिकृत नहीं होते हैं, तो आप उन्हें अनुप्रयोग से बाहर कर देते हैं।
एली नोव

5
मुझे अब भी लगता है कि उस स्थिति में, System.exit () के बजाय एप्लिकेशन से वापस आने का एक अच्छा तरीका होना चाहिए।
थॉमस ओवेन्स

28
यदि आप मुख्य () का परीक्षण कर रहे हैं, तो यह System.exit () को कॉल करने के लिए सही समझ में आता है। हमें एक आवश्यकता है कि त्रुटि पर एक बैच प्रक्रिया 1 के साथ बाहर निकलना चाहिए और 0. के साथ सफलता से बाहर निकलना चाहिए
मैथ्यू Farwell

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

जवाबों:


217

वास्तव में, Derkeiler.com सुझाव देता है:

  • क्यों System.exit()?

System.exit (जो कुछ भी) के साथ समाप्त करने के बजाय, अनियंत्रित अपवाद क्यों नहीं फेंकें? सामान्य उपयोग में यह JVM के अंतिम-खंदक पकड़ने वाले के लिए सभी तरह से बाहर निकल जाएगा और आपकी स्क्रिप्ट को बंद कर देगा (जब तक कि आप इसे रास्ते में कहीं पकड़ने का फैसला नहीं करते हैं, जो किसी दिन उपयोगी हो सकता है)।

JUnit परिदृश्य में इसे JUnit फ्रेमवर्क द्वारा पकड़ा जाएगा, जो रिपोर्ट करेगा कि इस तरह का और ऐसा परीक्षण विफल हो गया और अगले के साथ आसानी से आगे बढ़ गया।

  • System.exit()वास्तव में JVM से बाहर निकलने से रोकें :

TestCase को एक सुरक्षा प्रबंधक के साथ चलाने के लिए संशोधित करने का प्रयास करें जो System.exit को कॉल करने से रोकता है, फिर SecurityException को पकड़ें।

public class NoExitTestCase extends TestCase 
{

    protected static class ExitException extends SecurityException 
    {
        public final int status;
        public ExitException(int status) 
        {
            super("There is no escape!");
            this.status = status;
        }
    }

    private static class NoExitSecurityManager extends SecurityManager 
    {
        @Override
        public void checkPermission(Permission perm) 
        {
            // allow anything.
        }
        @Override
        public void checkPermission(Permission perm, Object context) 
        {
            // allow anything.
        }
        @Override
        public void checkExit(int status) 
        {
            super.checkExit(status);
            throw new ExitException(status);
        }
    }

    @Override
    protected void setUp() throws Exception 
    {
        super.setUp();
        System.setSecurityManager(new NoExitSecurityManager());
    }

    @Override
    protected void tearDown() throws Exception 
    {
        System.setSecurityManager(null); // or save and restore original
        super.tearDown();
    }

    public void testNoExit() throws Exception 
    {
        System.out.println("Printing works");
    }

    public void testExit() throws Exception 
    {
        try 
        {
            System.exit(42);
        } catch (ExitException e) 
        {
            assertEquals("Exit status", 42, e.status);
        }
    }
}

अद्यतन दिसंबर 2012:

सिस्टम नियमों का उपयोग करते हुए टिप्पणियों में प्रस्ताव करेगा , परीक्षण कोड के लिए JUnit (4.9+) नियमों का एक संग्रह जो उपयोग करता है । इसका उल्लेख शुरू में स्टीफन बिर्कनर ने दिसंबर 2011 में अपने जवाब में किया था।java.lang.System

System.exit(…)

कहा जाता है ExpectedSystemExitकि सत्यापित करने के लिए नियम का उपयोग करें System.exit(…)
आप बाहर निकलने की स्थिति को भी सत्यापित कर सकते हैं।

उदाहरण के लिए:

public void MyTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void noSystemExit() {
        //passes
    }

    @Test
    public void systemExitWithArbitraryStatusCode() {
        exit.expectSystemExit();
        System.exit(0);
    }

    @Test
    public void systemExitWithSelectedStatusCode0() {
        exit.expectSystemExitWithStatus(0);
        System.exit(0);
    }
}

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

7
सुनिश्चित करें कि नीचे आंसू को ठीक से निष्पादित किया गया है, अन्यथा, आपका परीक्षण ग्रहण जैसे धावक में विफल हो जाएगा क्योंकि JUnit एप्लिकेशन बाहर नहीं निकल सकता है! :)
MetroidFan2002

6
मुझे सुरक्षा प्रबंधक का उपयोग करके समाधान पसंद नहीं है। इसे टेस्ट करने के लिए मेरे लिए एक हैक की तरह लगता है।
निकोलई पुनश्चलिंग

7
यदि आप 4.7 या इसके बाद के संस्करण का उपयोग कर रहे हैं, तो अपने System.exit कॉल को कैप्चर करने के लिए लाइब्रेरी डील करें। सिस्टम रूल्स - stefanbirkner.github.com/system-rules
होगा

6
"System.exit (जो कुछ भी) के साथ समाप्त करने के बजाय, एक अनियंत्रित अपवाद क्यों नहीं फेंकें?" - क्योंकि मैं कमांड लाइन तर्क प्रोसेसिंग फ्रेमवर्क का उपयोग कर रहा हूं जो कि System.exitजब भी एक अवैध कमांड लाइन तर्क की आपूर्ति की जाती है।
एडम पार्किन

108

लाइब्रेरी सिस्टम लैंबडा में एक विधि है catchSystemExit। इस नियम के साथ आप कोड का परीक्षण करने में सक्षम हैं, जिसे System.exit (...) कहते हैं।

public void MyTest {
    @Test
    public void systemExitWithArbitraryStatusCode() {
        SystemLambda.catchSystemExit(() -> {
            //the code under test, which calls System.exit(...);
        });
    }
}

जावा 5 से 7 के लिए लाइब्रेरी सिस्टम रूल्स में एक JUnit नियम है, जिसे ExpectedSystemExit कहा जाता है। इस नियम से आप कोड का परीक्षण करने में सक्षम हैं, जो System.exit (...) को कॉल करता है:

public void MyTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void systemExitWithArbitraryStatusCode() {
        exit.expectSystemExit();
        //the code under test, which calls System.exit(...);
    }

    @Test
    public void systemExitWithSelectedStatusCode0() {
        exit.expectSystemExitWithStatus(0);
        //the code under test, which calls System.exit(0);
    }
}

पूर्ण प्रकटीकरण: मैं दोनों पुस्तकालयों का लेखक हूं।


3
उत्तम। सुरुचिपूर्ण। मेरे मूल कोड की एक सिलाई को बदलने या सुरक्षा प्रबंधक के साथ खेलने की ज़रूरत नहीं थी। यह शीर्ष उत्तर होना चाहिए!
विल

2
यह शीर्ष उत्तर होना चाहिए। System.exit के उचित उपयोग पर अंतहीन बहस के बजाय, सीधे बिंदु पर। इसके अलावा, JUnit के पालन में एक लचीला समाधान तो हमें पहिया को सुदृढ़ करने या सुरक्षा प्रबंधक के साथ गड़बड़ करने की आवश्यकता नहीं है।
एल। होलांडा

@LeoHolanda क्या यह समाधान उसी "समस्या" से ग्रस्त नहीं है, जिसका उपयोग आपने मेरे उत्तर पर गलत जवाब देने के लिए किया था? इसके अलावा, TestNG उपयोगकर्ताओं के बारे में क्या?
रोजेरियो

2
@LeoHolanda तुम सही हो; नियम के कार्यान्वयन को देखते हुए, मैं अब देखता हूं कि यह एक कस्टम सुरक्षा प्रबंधक का उपयोग करता है जो "सिस्टम से बाहर निकलने" की जाँच के लिए कॉल को अपवाद कहता है, इसलिए परीक्षण समाप्त होता है। मेरे जवाब में जोड़ा गया उदाहरण परीक्षण दोनों आवश्यकताओं (System.exit के साथ उचित सत्यापन को बुलाया / बुलाया नहीं), BTW को संतुष्ट करता है। का उपयोग ExpectedSystemRuleअच्छा है, निश्चित रूप से; समस्या यह है कि इसके लिए एक अतिरिक्त 3-पार्टी लाइब्रेरी की आवश्यकता होती है जो वास्तविक-विश्व उपयोगिता के संदर्भ में बहुत कम प्रदान करती है, और JUnit- विशिष्ट है।
रोजेरियो

2
नहीं है exit.checkAssertionAfterwards()
स्टीफन बिर्कनर

31

इस तरीके में "ExitManager" को इंजेक्ट करने के बारे में:

public interface ExitManager {
    void exit(int exitCode);
}

public class ExitManagerImpl implements ExitManager {
    public void exit(int exitCode) {
        System.exit(exitCode);
    }
}

public class ExitManagerMock implements ExitManager {
    public bool exitWasCalled;
    public int exitCode;
    public void exit(int exitCode) {
        exitWasCalled = true;
        this.exitCode = exitCode;
    }
}

public class MethodsCallExit {
    public void CallsExit(ExitManager exitManager) {
        // whatever
        if (foo) {
            exitManager.exit(42);
        }
        // whatever
    }
}

उत्पादन कोड ExitManagerImpl का उपयोग करता है और परीक्षण कोड ExitManagerMock का उपयोग करता है और यह जांच कर सकता है कि क्या बाहर निकलें () को बुलाया गया था और किस निकास कोड के साथ।


1
+1 अच्छा समाधान। यह भी लागू करना आसान है अगर आप स्प्रिंग का उपयोग कर रहे हैं क्योंकि एग्जिटमैन एक सरल घटक बन जाता है। बस इस बात से अवगत रहें कि आपको यह सुनिश्चित करने की ज़रूरत है कि आपका कोड एक्ज़िटमैनेजर.एक्सिट () कॉल के बाद निष्पादित नहीं करता है। जब आपका कोड एक नकली ExitManager के साथ परीक्षण किया जा रहा है तो कोड वास्तव में exitManager.exit पर कॉल करने के बाद बाहर नहीं निकलेगा।
23:68 पर Joman68

31

आप वास्तव में, JUnit परीक्षण में विधि का मजाक उड़ा सकते हैं या रोक सकते हैंSystem.exit

उदाहरण के लिए, JMockit का उपयोग करके आप लिख सकते हैं (अन्य तरीके भी हैं):

@Test
public void mockSystemExit(@Mocked("exit") System mockSystem)
{
    // Called by code under test:
    System.exit(); // will not exit the program
}


EDIT: वैकल्पिक परीक्षण (नवीनतम JMockit API का उपयोग करके) जो किसी भी कोड को कॉल के बाद चलने की अनुमति नहीं देता है System.exit(n):

@Test(expected = EOFException.class)
public void checkingForSystemExitWhileNotAllowingCodeToContinueToRun() {
    new Expectations(System.class) {{ System.exit(anyInt); result = new EOFException(); }};

    // From the code under test:
    System.exit(1);
    System.out.println("This will never run (and not exit either)");
}

2
वोट डाउन कारण: इस समाधान के साथ समस्या यह है कि यदि System.exit कोड में अंतिम पंक्ति नहीं है (अर्थात यदि स्थिति के अंदर), तो कोड चलता रहेगा।
एल। हॉलैंडा

@ लॉहोलैंड ने परीक्षण का एक संस्करण जोड़ा जो exitकॉल के बाद कोड को चलाने से रोकता है (यह नहीं कि यह वास्तव में एक समस्या थी, आईएमओ)।
रोजेरियो

हो सकता है कि एस्टर कथन के साथ अंतिम System.out.println () को प्रतिस्थापित करना अधिक उचित हो?
user515655

"@Mocked (" बाहर निकलें ")" JMockit 1.43 के साथ अब काम नहीं करता है: "विशेषता मान एनोटेशन प्रकार के लिए अपरिभाषित है Mocked" डॉक्स: "तीन अलग-अलग नकली घोषणाएँ हैं जिनका उपयोग हम मॉक फ़ील्ड घोषित करते समय और कर सकते हैं पैरामीटर्स: @Mocked, जो मॉक क्लास के सभी मौजूदा और भविष्य के उदाहरणों पर सभी तरीकों और कंस्ट्रक्टर्स का मज़ाक उड़ाएगा (इसका उपयोग करने वाले परीक्षणों की अवधि के लिए); " jmockit.github.io/tutorial/Mocking.html#mocked
Thorsten

क्या आपने वास्तव में अपने सुझाव की कोशिश की? मुझे मिलता है: "java.lang.IllegalArgumentException: Class java.lang.System नकली नहीं है" Runtimeका मज़ाक उड़ाया जा सकता है, लेकिन ग्रैडल System.exitमें चल रहे टेस्ट में मेरे परिदृश्य में कम से कम रोक नहीं है ।
थोरस्टेन शॉनिंग

20

हमारे कोड बेस में हमने जो एक ट्रिक इस्तेमाल की थी, उसमें कॉल करना था। System.exit () को रननेबल इम्पैक्ट में इनकैप्सुलेट किया जाना चाहिए, जो कि डिफॉल्ट रूप से उपयोग किए जाने वाले प्रश्न में विधि है। यूनिट टेस्ट के लिए, हम एक अलग मॉक रननेबल सेट करते हैं। कुछ इस तरह:

private static final Runnable DEFAULT_ACTION = new Runnable(){
  public void run(){
    System.exit(0);
  }
};

public void foo(){ 
  this.foo(DEFAULT_ACTION);
}

/* package-visible only for unit testing */
void foo(Runnable action){   
  // ...some stuff...   
  action.run(); 
}

... और JUnit परीक्षण विधि ...

public void testFoo(){   
  final AtomicBoolean actionWasCalled = new AtomicBoolean(false);   
  fooObject.foo(new Runnable(){
    public void run(){
      actionWasCalled.set(true);
    }   
  });   
  assertTrue(actionWasCalled.get()); 
}

क्या इसे वे निर्भरता इंजेक्शन कहते हैं?
थॉमस अहले

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

6

एक मॉक-सक्षम वर्ग बनाएं जो System.exit () को लपेटता है

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

System.exit पर परीक्षण निष्पादन रोकना ()

संकट:

// do thing1
if(someCondition) {
    System.exit(1);
}
// do thing2
System.exit(0)

एक नकली Sytem.exit()निष्पादन को समाप्त नहीं करेगा। यह बुरा है यदि आप परीक्षण करना चाहते हैं जो thing2निष्पादित नहीं है।

उपाय:

आपको इस कोड को रिफैक्ट करना चाहिए जैसा कि मार्टिन द्वारा सुझाया गया है :

// do thing1
if(someCondition) {
    return 1;
}
// do thing2
return 0;

और System.exit(status)कॉलिंग फंक्शन में करें। यह आपको अपने सभी System.exit()एस को एक जगह या उसके पास रखने के लिए मजबूर करता है main()। यह System.exit()आपके तर्क के अंदर गहरी कॉल करने की तुलना में क्लीनर है ।

कोड

आवरण:

public class SystemExit {

    public void exit(int status) {
        System.exit(status);
    }
}

मुख्य:

public class Main {

    private final SystemExit systemExit;


    Main(SystemExit systemExit) {
        this.systemExit = systemExit;
    }


    public static void main(String[] args) {
        SystemExit aSystemExit = new SystemExit();
        Main main = new Main(aSystemExit);

        main.executeAndExit(args);
    }


    void executeAndExit(String[] args) {
        int status = execute(args);
        systemExit.exit(status);
    }


    private int execute(String[] args) {
        System.out.println("First argument:");
        if (args.length == 0) {
            return 1;
        }
        System.out.println(args[0]);
        return 0;
    }
}

परीक्षा:

public class MainTest {

    private Main       main;

    private SystemExit systemExit;


    @Before
    public void setUp() {
        systemExit = mock(SystemExit.class);
        main = new Main(systemExit);
    }


    @Test
    public void executeCallsSystemExit() {
        String[] emptyArgs = {};

        // test
        main.executeAndExit(emptyArgs);

        verify(systemExit).exit(1);
    }
}

5

मुझे पहले से दिए गए कुछ उत्तर पसंद हैं, लेकिन मैं एक अलग तकनीक का प्रदर्शन करना चाहता था जो परीक्षण के तहत विरासत कोड प्राप्त करते समय अक्सर उपयोगी होता है। दिए गए कोड की तरह:

public class Foo {
  public void bar(int i) {
    if (i < 0) {
      System.exit(i);
    }
  }
}

आप एक विधि बनाने के लिए एक सुरक्षित रीफैक्टरिंग कर सकते हैं जो System.exit कॉल को लपेटता है:

public class Foo {
  public void bar(int i) {
    if (i < 0) {
      exit(i);
    }
  }

  void exit(int i) {
    System.exit(i);
  }
}

फिर आप अपने परीक्षण के लिए एक नकली बना सकते हैं जो निकास से बाहर निकलता है:

public class TestFoo extends TestCase {

  public void testShouldExitWithNegativeNumbers() {
    TestFoo foo = new TestFoo();
    foo.bar(-1);
    assertTrue(foo.exitCalled);
    assertEquals(-1, foo.exitValue);
  }

  private class TestFoo extends Foo {
    boolean exitCalled;
    int exitValue;
    void exit(int i) {
      exitCalled = true;
      exitValue = i;
    }
}

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


2
बाहर निकलने () को बुलाए जाने पर यह टेक्नॉनिक्स कोनोल प्रवाह को नहीं रोकता है। इसके बजाय एक अपवाद का उपयोग करें।
एंड्रिया फ्रांसिया

3

एपीआई पर एक त्वरित नज़र, दिखाता है कि System.exit अपवाद जाप फेंक सकता है। अगर कोई सुरक्षाकर्मी vm के बंद होने से मना करता है। शायद इस तरह के एक प्रबंधक को स्थापित करने के लिए एक समाधान होगा।


3

जावा VM को चालू करने से रोकने के लिए आप जावा SecurityManager का उपयोग कर सकते हैं। निम्नलिखित कोड को वह करना चाहिए जो आप चाहते हैं:

SecurityManager securityManager = new SecurityManager() {
    public void checkPermission(Permission permission) {
        if ("exitVM".equals(permission.getName())) {
            throw new SecurityException("System.exit attempted and blocked.");
        }
    }
};
System.setSecurityManager(securityManager);

हम्म। System.exit डॉक्स विशेष रूप से कहते हैं कि checkExit (int) को बुलाया जाएगा, ना कि checkPermission नाम के साथ = "exitVit"। मुझे आश्चर्य है कि क्या मुझे दोनों को ओवरराइड करना चाहिए?
क्रिस कॉनवे

अनुमति नाम वास्तव में एक्ज़िट वीएम लगता है। (स्टेटसकोड), यानी एग्ज़िट वीएम .0 - कम से कम ओएसएक्स पर मेरे हालिया परीक्षण में।
मार्क डेरिकुट जूल

3

JUnit 4 पर चलने के लिए VonC के उत्तर के लिए, मैंने कोड को इस प्रकार संशोधित किया है

protected static class ExitException extends SecurityException {
    private static final long serialVersionUID = -1982617086752946683L;
    public final int status;

    public ExitException(int status) {
        super("There is no escape!");
        this.status = status;
    }
}

private static class NoExitSecurityManager extends SecurityManager {
    @Override
    public void checkPermission(Permission perm) {
        // allow anything.
    }

    @Override
    public void checkPermission(Permission perm, Object context) {
        // allow anything.
    }

    @Override
    public void checkExit(int status) {
        super.checkExit(status);
        throw new ExitException(status);
    }
}

private SecurityManager securityManager;

@Before
public void setUp() {
    securityManager = System.getSecurityManager();
    System.setSecurityManager(new NoExitSecurityManager());
}

@After
public void tearDown() {
    System.setSecurityManager(securityManager);
}

3

आप रनटाइम उदाहरण की जगह System.exit (..) का परीक्षण कर सकते हैं। जैसे TestNG + Mockito:

public class ConsoleTest {
    /** Original runtime. */
    private Runtime originalRuntime;

    /** Mocked runtime. */
    private Runtime spyRuntime;

    @BeforeMethod
    public void setUp() {
        originalRuntime = Runtime.getRuntime();
        spyRuntime = spy(originalRuntime);

        // Replace original runtime with a spy (via reflection).
        Utils.setField(Runtime.class, "currentRuntime", spyRuntime);
    }

    @AfterMethod
    public void tearDown() {
        // Recover original runtime.
        Utils.setField(Runtime.class, "currentRuntime", originalRuntime);
    }

    @Test
    public void testSystemExit() {
        // Or anything you want as an answer.
        doNothing().when(spyRuntime).exit(anyInt());

        System.exit(1);

        verify(spyRuntime).exit(1);
    }
}

2

ऐसे वातावरण हैं जहां कॉलिंग प्रोग्राम (जैसे MS बैच में ERRORLEVEL) द्वारा दिए गए निकास निकास कोड का उपयोग किया जाता है। हमारे पास हमारे कोड में ऐसा करने वाले मुख्य तरीकों के परीक्षण हैं, और हमारा दृष्टिकोण एक समान SecurityManager ओवरराइड का उपयोग करने के लिए किया गया है जैसा कि यहां अन्य परीक्षणों में उपयोग किया गया है।

कल रात मैंने सिक्योरिटी मैनेजर कोड को छिपाने के लिए Junit @Rule एनोटेशन का उपयोग करते हुए एक छोटा सा JAR डाला, साथ ही अपेक्षित रिटर्न कोड के आधार पर अपेक्षाओं को जोड़ा। http://code.google.com/p/junitsystemrules/


2

सबसे समाधान होगा

  • परीक्षण (विधि, संपूर्ण रन नहीं) को समाप्त करने का क्षण System.exit()कहा जाता है
  • पहले से स्थापित की उपेक्षा करें SecurityManager
  • कभी-कभी परीक्षण ढांचे के लिए काफी विशिष्ट होते हैं
  • प्रति परीक्षण के मामले में अधिकतम एक बार उपयोग करने के लिए प्रतिबंधित है

इस प्रकार, अधिकांश समाधान उन स्थितियों के लिए अनुकूल नहीं हैं जहाँ:

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

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

निम्न वर्ग एक विधि प्रदान करता है assertExits(int expectedStatus, Executable executable)जो System.exit()निर्दिष्ट statusमूल्य के साथ कहा जाता है , और परीक्षण इसके बाद भी जारी रह सकता है। यह उसी तरह काम करता है जैसे कि JUnit 5assertThrows । यह एक मौजूदा सुरक्षा प्रबंधक का भी सम्मान करता है।

एक शेष समस्या है: जब परीक्षण के तहत कोड एक नया सुरक्षा प्रबंधक स्थापित करता है जो परीक्षण द्वारा निर्धारित सुरक्षा प्रबंधक को पूरी तरह से बदल देता है। SecurityManagerमेरे लिए ज्ञात अन्य सभी- आधारित समाधान एक ही समस्या से ग्रस्त हैं।

import java.security.Permission;

import static java.lang.System.getSecurityManager;
import static java.lang.System.setSecurityManager;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

public enum ExitAssertions {
    ;

    public static <E extends Throwable> void assertExits(final int expectedStatus, final ThrowingExecutable<E> executable) throws E {
        final SecurityManager originalSecurityManager = getSecurityManager();
        setSecurityManager(new SecurityManager() {
            @Override
            public void checkPermission(final Permission perm) {
                if (originalSecurityManager != null)
                    originalSecurityManager.checkPermission(perm);
            }

            @Override
            public void checkPermission(final Permission perm, final Object context) {
                if (originalSecurityManager != null)
                    originalSecurityManager.checkPermission(perm, context);
            }

            @Override
            public void checkExit(final int status) {
                super.checkExit(status);
                throw new ExitException(status);
            }
        });
        try {
            executable.run();
            fail("Expected System.exit(" + expectedStatus + ") to be called, but it wasn't called.");
        } catch (final ExitException e) {
            assertEquals(expectedStatus, e.status, "Wrong System.exit() status.");
        } finally {
            setSecurityManager(originalSecurityManager);
        }
    }

    public interface ThrowingExecutable<E extends Throwable> {
        void run() throws E;
    }

    private static class ExitException extends SecurityException {
        final int status;

        private ExitException(final int status) {
            this.status = status;
        }
    }
}

आप इस तरह वर्ग का उपयोग कर सकते हैं:

    @Test
    void example() {
        assertExits(0, () -> System.exit(0)); // succeeds
        assertExits(1, () -> System.exit(1)); // succeeds
        assertExits(2, () -> System.exit(1)); // fails
    }

यदि आवश्यक हो तो कोड को आसानी से JUnit 4, TestNG, या किसी अन्य ढांचे में पोर्ट किया जा सकता है। एकमात्र फ्रेमवर्क-विशिष्ट तत्व परीक्षण में विफल हो रहा है। इसे आसानी से कुछ फ्रेमवर्क-इंडिपेंडेंट (एक जून 4 के अलावा) में बदला जा सकता है Rule

सुधार के लिए जगह है, उदाहरण के लिए, assertExits()अनुकूलन संदेशों के साथ ओवरलोडिंग ।


1

Runtime.exec(String command)एक अलग प्रक्रिया में जेवीएम शुरू करने के लिए उपयोग करें ।


3
आप परीक्षण के तहत कक्षा के साथ कैसे बातचीत करेंगे, अगर यह यूनिट टेस्ट से अलग प्रक्रिया में है?
डीएनए

1
99% मामलों में System.exit एक मुख्य विधि के अंदर है। इसलिए, आप उनके साथ कमांड लाइन तर्क (यानी args) द्वारा बातचीत करते हैं।
एल। हॉलैंडा

1

SecurityManagerसमाधान के साथ एक मामूली समस्या है । कुछ तरीके, जैसे JFrame.exitOnClose, भी कहते हैं SecurityManager.checkExit। मेरे आवेदन में, मैं नहीं चाहता था कि कॉल विफल हो जाए, इसलिए मैंने उपयोग किया

Class[] stack = getClassContext();
if (stack[1] != JFrame.class && !okToExit) throw new ExitException();
super.checkExit(status);

0

System.exit () को कॉल करना एक बुरा अभ्यास है, जब तक कि यह एक मुख्य () के अंदर नहीं किया जाता है। इन विधियों को एक अपवाद फेंकना चाहिए, जो अंततः, आपके मुख्य () द्वारा पकड़ा जाता है, जो तब System.exit को उपयुक्त कोड के साथ कॉल करता है।


8
हालांकि इस सवाल का जवाब नहीं है। क्या होगा यदि परीक्षण किया जा रहा है समारोह अंततः मुख्य विधि है? इसलिए System.exit () को कॉल करना एक डिज़ाइन परिप्रेक्ष्य से मान्य और ठीक हो सकता है। आप इसके लिए टेस्ट केस कैसे लिखते हैं?
एली

3
आपको मुख्य विधि का परीक्षण नहीं करना चाहिए क्योंकि मुख्य विधि को बस किसी भी तर्क को लेना चाहिए, उन्हें एक पार्सर विधि से पारित करना चाहिए, और फिर एप्लिकेशन को प्रारंभ करना चाहिए। परीक्षण की जाने वाली मुख्य विधि में कोई तर्क नहीं होना चाहिए।
थॉमस ओवेन्स

5
@ ईली: इस प्रकार के प्रश्नों में दो मान्य उत्तर होते हैं। एक प्रश्न का उत्तर दे रहा है, और एक पूछ रहा है कि प्रश्न आधारित क्यों था। दोनों प्रकार के उत्तर एक बेहतर समझ देते हैं, और विशेष रूप से दोनों एक साथ।
रनरास
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.