使用JUnit测试控制台输出

时间:2014-12-10 23:14:51

标签: java junit

我正在尝试使用JUnit测试另一个程序的控制台输出。我按照here

中给出的答案

这是我的JUnit类

import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.*;

public class MainTest {
        private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
        private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();

        @Before
        public void setup() {
                System.setOut(new PrintStream(outContent));
                System.setErr(new PrintStream(errContent));
        }

        @Test
        public void test01_initTest() {
                String[] arguments = {"a", "b"};
                Main.main(arguments);
                String expected = "hello";
                assertTrue(expected == outContent.toString());
        }

        @After
        public void cleanUpStreams() {
            System.setOut(null);
            System.setErr(null);
        }
}

当我在eclipse中运行此程序时,我既没有看到控制台输出也没有测试运行结束。但是,如果我直接从test01_initTest()在控制台上打印输出,则测试通过。要检查Main.main()是否正在打印,我还尝试了以下代码,在这种情况下我确实看到了控制台输出

import static org.junit.Assert.*;
import org.junit.*;

public class MainTest {
        @Test
        public void test01_initTest() {
                String[] arguments = {"a", "b"};
                Main.main(arguments);
        }
}

我厌倦了很多事情,但我无法弄清楚我在这里做错了什么。有人可以帮帮我。

1 个答案:

答案 0 :(得分:1)

如果您正在测试的代码调用System.exit(),则JUnit无法正常完成。

如果主线程终止,JUnit将挂起。

解决方法是使用SecurityManager来阻止对System.exit()

的调用

在这个其他StackOverflow答案中,我描述了如何编写这样的SecurityManager:

Getting list of tests from JUnit command line

(此处复制的代码以防万一)。

当调用TriedToExitException时,此SecurityManager将抛出自定义System.exit(),并保留退出代码,以便捕获异常并测试是否使用了正确的退出代码。

/**
 * A special implementation of {@link SecurityManager}
 * that will throw a {@link TriedToExitException}
 * if {@link System#exit(int)} is called.
 * This is useful for testing main methods
 * without shutting
 * down the jvm running junit.
 * Tests can catch {@link TriedToExitException}
 * to figure out what exit code was set.
 */
public static final SecurityManager NON_EXITABLE_MANAGER = new SecurityManager(){

    @Override
    public void checkPermission(Permission perm) {
        //allow everything
    }
    /**
     * Throws a {@link TriedToExitException} instead of exiting.
     * <p/>
     * {@inheritDoc}
     */
    @Override
    public void checkExit(int status) {
        throw new TriedToExitException(status);
    }

};

public static final class TriedToExitException extends SecurityException{
    private static final long serialVersionUID = 1L;
    private final int exitCode;

    public TriedToExitException(int exitCode){
        this.exitCode=exitCode;
    }

    @Override
    public String getMessage() {
        return String.format("tried to System.exit(%d)",exitCode);
    }

    public int getExitCode() {
        return exitCode;
    }

然后在您的单元测试中,将SecurityManager替换为停止调用退出的版本(并保存旧的SecurityManager,以便我们可以在测试后恢复它)

private SecurityManager previousManager=null;


@Before
protected void replaceManager() throws Throwable {
    previousManager = System.getSecurityManager();
    System.setSecurityManager(NON_EXITABLE_MANAGER);
}

@After
protected void restoreManager() {
    System.setSecurityManager(previousManager);
}

 @Test
public void testCallToExit() throws IOException{

     try{
       //something that calls System.exit
        fail("should call exit");
     }catch(TriedToExitException e){
         //make sure we get correct exit code
         assertEquals(-1, e.getExitCode());

     }
}