JUnit测试中可以用assertTrue,assertEquals等断言对代码进行简单的测试:如返回的布尔类型是否为真,所得的数据结果是否与预想的一样,有时程序可能会为健壮性做一些向控制台输出和强制退出的操作,对于这样的功能用JUnit的断言可能不太直观,但实际上也是可行的。
这里以一个实现图的程序为例,Graph是一个图,可以通过addVertex操作向这张图中添加结点,对于同样名字的结点,addVertex方法会向控制台输出一句“名字重复”,并通过System.exit(0)的方式直接退出程序。
其中获得控制台的输出相对容易一些,只需要在控制台输出之前,将标准输出流指定到一个位置存储再进行比较就可以了:
private PrintStream console = null;
private ByteArrayOutputStream bytes = null;
@Before
public void setUp() {
bytes = new ByteArrayOutputStream();// 把标准输出指定到ByteArrayOutputStream中
console = System.out;// 获取System.out 输出流的句柄
System.setOut(new PrintStream(bytes));// 将原本输出到控制台Console的字符流重定向到bytes
}
@After
public void tearDown() {
System.setOut(console);
}
最后在@Test中将得到的控制台输出byte通过toString方法转化为字符串与预想结果“名字重复”进行比较就可以了。
这个过程中的问题是,由于程序输出完错误信息直接通过System.exit(0)退出,控制台被关闭,从而无法得到正确的结果,查阅资料后得知,System.exit其实会调用SecurityManager.checkPermission方法来校验权限,这个时候只要在checkPermission方法内部抛出一个异常,就能把System.exit后续的逻辑打断了,也就是不会退出了。
完整代码如下:
private PrintStream console = null;
private ByteArrayOutputStream bytes = null;
@Before
public void setUp() {
final SecurityManager securityManager = new SecurityManager() {
public void checkPermission(Permission permission) {
/**
* 这里permission.getName()会返回exitVM. + status的形式 例如System.exit(0) 这里会是exitVM.0
* System.exit(1) 这里会是exitVM.1 这里不关系怎么退出的,只需要捕捉到,并不让他退出,继续运行我的测试代码就好了
*/
if (permission.getName().startsWith("exitVM")) {
throw new AccessControlException("");
}
}
};
System.setSecurityManager(securityManager);
bytes = new ByteArrayOutputStream();// 把标准输出指定到ByteArrayOutputStream中
console = System.out;// 获取System.out 输出流的句柄
System.setOut(new PrintStream(bytes));// 将原本输出到控制台Console的字符流重定向到bytes
}
@After
public void tearDown() {
System.setOut(console);
}
@Test
public void testaddVertexRepeat() {
try {
Graph graph = new Graph();
Vertex lee = new Vertex("lee");
Vertex wong = new Vertex("lee");
graph.addVertex(lee);
graph.addVertex(wong);
} catch (RuntimeException e) {
} finally {
System.setSecurityManager(null);
}
assertEquals("名字重复", bytes.toString());
}