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

我有一些方法应该调用某些输入。不幸的是,测试这些情况会导致JUnit终止!将方法调用放在新的线程中似乎没有帮助,因为会终止JVM,而不仅仅是当前线程。是否有任何常见的模式来处理这个问题?例如,我可以将存根替换为 ?System.exit()System.exit()System.exit()

[编辑]有问题的类实际上是一个命令行工具,我试图在JUnit中进行测试。也许JUnit根本不是这项工作的正确工具?欢迎对补充回归测试工具提出建议(最好是与JUnit和EclEmma很好地集成的东西)。


答案 1

事实上,Derkeiler.com 建议:

  • 为什么?System.exit()

为什么不抛出一个未经检查的异常,而不是以 System.exit(whateverValue) 结束呢?在正常使用中,它会一直漂移到JVM的最后一个捕手并关闭你的脚本(除非你决定沿途的某个地方捕捉它,这有一天可能会有用)。

在 JUnit 场景中,它将被 JUnit 框架捕获,该框架将报告某某测试失败,并顺利进行下一个测试。

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

尝试修改 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 年 12 月更新:

Will 在使用 System Rules 的注释中提出了一个 JUnit(4.9+) 规则的集合,用于测试使用 .
斯特凡·伯克纳(Stefan Birkner)在2011年12月的回答中最初提到了这一点。java.lang.System

System.exit(…)

使用“预期系统存在”规则验证是否被调用。
您也可以验证退出状态。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);
    }
}

答案 2

系统 Lambda 有一个方法。使用此规则,您可以测试调用 System.exit(...) 的代码:catchSystemExit

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


    @Test
    public void systemExitWithSelectedStatusCode0() {
        int status = SystemLambda.catchSystemExit(() -> {
            //the code under test, which calls System.exit(0);
        });

        assertEquals(0, status);
    }
}

对于 Java 5 到 7,库系统规则有一个名为 ExpectedSystemExit 的 JUnit 规则。使用此规则,您可以测试调用 System.exit(...) 的代码:

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

完全披露:我是这两个库的作者。


推荐