的Java:如何测试调用System.exit()的方法?的Java:如何测试调用System.ex

2019-05-10 13:17发布

我有一个应该叫几个方法System.exit()某些输入。 不幸的是,测试这种情况下会导致JUnit来终止! 在一个新的线程把方法调用似乎并没有帮助,因为System.exit()终止JVM,而不仅仅是当前线程。 是否有处理这个任何共同的模式? 例如,我可以替补多的存根System.exit()

[编辑]所讨论的类实际上是一个命令行工具我试图内JUnit测试。 也许JUnit是根本就没有为工作的工具? 为补充回归测试工具的建议,欢迎(最好的东西,使用JUnit和EclEmma很好地结合)。

Answer 1:

事实上, Derkeiler.com建议:

  • 为什么System.exit()

相反,与System.exit(whateverValue)终止的,为什么不抛出一个未经检查的异常? 在正常使用它会飘所有的出路JVM的最后一搏捕手和关闭脚本下(除非你决定某处捕获它一路走来,这可能是有用的一天)。

在JUnit的情况下,将通过JUnit框架,这将报告说,某某,这样的测试失败,并传递给下一个平滑移动被抓。

  • 防止System.exit()实际退出JVM:

尝试修改测试用例来用防止调用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年12月:

请问提出的意见使用系统规则 ,JUnit的集合(4.9+)为它使用测试代码规则java.lang.System
这最初是由提到斯特凡·伯克纳在他的回答 2011年12月。

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);
    }
}


Answer 2:

图书馆系统规则有一个名为ExpectedSystemExit JUnit的规则。 有了这个规则,你可以测试代码,该代码调用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);
    }
}

披露:我是该库的作者。



Answer 3:

实际上,你可以嘲笑或存根出System.exit方法,在JUnit测试。

例如,使用JMockit你可以写(也有其他方面也):

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


编辑:另类测试(使用最新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)");
}


Answer 4:

如何注入“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并且能够检查是否出口()被调用并与退出代码。



Answer 5:

我们在我们的代码库中使用的一种伎俩是让调用System.exit()的在一个Runnable IMPL进行封装,它默认使用有问题的方法。 单元测试,我们制定了不同的模拟Runnable接口。 事情是这样的:

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()); 
}


Answer 6:

我喜欢一些已经给出答案,但我想证明不同的技术测试得到的遗留代码的时候,往往是有用的。 鉴于这样的代码:

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;
    }
}

这是用于替代测试用例行为的通用技术,重构旧代码时,我用它所有的时间。 它通常不是,我要离开的事情,而是一个中间步骤来测试下得到现有的代码。



Answer 7:

创建一个包装System.exit一个模拟能级()

我同意EricSchaefer 。 但是,如果你使用的是一个很好的模拟框架的Mockito一个简单的具体类就足够了,没有必要的接口和两个实现。

上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()在一个地方S IN或附近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);
    }
}


Answer 8:

快速浏览一下的API,显示System.exit可以ESP抛出异常。 如果安全管理器禁止虚拟机的关机。 也许一个解决办法是安装这样的经理。



Answer 9:

您可以使用Java安全管理器,以防止当前线程关闭Java虚拟机。 下面的代码应该做你想要什么:

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);


Answer 10:

对于VonC的回答对JUnit 4中运行,我已经修改了代码如下

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);
}


Answer 11:

在有些情况下返回的退出代码用于由调用程序(如在ERRORLEVEL MS批次)环境。 我们围绕在我们的代码做到这一点的主要方法测试,我们的方法是使用一个类似的安全管理器覆盖在其他测试中使用这里。

昨晚我放在一起使用JUnit @rule注解是隐藏安全管理器代码,以及添加基于预期收益代码预期一个小罐子。 http://code.google.com/p/junitsystemrules/



Answer 12:

你可以用更换运行实例测试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);
    }
}


Answer 13:

调用System.exit()是一个不好的做法,除非它里面主要做()。 这些方法应该扔这,最终是由你的主(捕获)异常,谁然后用适当的代码调用System.exit。



Answer 14:

使用Runtime.exec(String command)在一个单独的进程来启动JVM。



Answer 15:

没有与一个小问题SecurityManager解决方案。 一些方法,如JFrame.exitOnClose ,也叫SecurityManager.checkExit 。 在我的应用程序,我不希望这样的呼叫失败,所以我用

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


文章来源: Java: How to test methods that call System.exit()?