JUnit 测试含有控制台输入的方法
使用JUnit 进行单元测试时,有时会遇到被测试方法存在控制台输入的情况,这样在测试时需要不厌其烦地手动输入测试用例,显然违背了自动测试的初衷。
本文简介两种通过输入重定向实现自动测试控制台输入的方法。
代码:https://github.com/1161140118/TEST-code/tree/master/src/KeyBoardInputTransform
下面是待测试类ReadFromConsole,其中的ReadAndShow为我们要测试的含有控制台输入的方法。
1 public class ReadFromConsole { 2 private Scanner in = new Scanner(System.in); 3 4 public void setIn(ByteArrayInputStream baIn) { 5 this.in = new Scanner(baIn); 6 } 7 8 public void ReadAndShow() { 9 String line = in.nextLine(); 10 System.out.println("This is the input: \n"+line); 11 } 12 13 public static void main(String[] args) { 14 ReadFromConsole r = new ReadFromConsole(); 15 r.ReadAndShow(); 16 } 17 18 }
首先简单了解下输入重定向 System.setIn() 方法
Java.lang.System.setIn() 方法重新分配标准输入流
/** * 函数声明 * @param in - 新的标准输入流 */ public static void setIn(InputStream in);
示例
1 private static ByteArrayInputStream in; 2 private static Scanner scanner; 3 4 public static void input() { 5 String data = "Hello, World!"; 6 InputStream stdin = System.in; 7 try { 8 // 输入重定向 9 System.setIn(new ByteArrayInputStream(data.getBytes())); 10 // 此时 System.in 为ByteArrayInputStream对象 11 scanner = new Scanner(System.in); 12 System.out.println(scanner.nextLine()); 13 } finally { 14 System.setIn(stdin); 15 } 16 }
调用input方法,输出
Hello, World!
本次调用没有要求从控制台输入语句,直接输出data字符串。
言归正传 讨论我们的解决问题方法:
方案1 当被调用类存在Scanner对象时,可临时创建相关方法使得类内输入被重定向为指定字符串的字节流,如下
public class ReadFromConsole { private Scanner in = new Scanner(System.in); /** * 获得测试所需标准输入字符串,作为Scanner对象读取源 * @param baIn - 测试所需标准输入字符串的字节流,由测试类传入 */ public void setIn(ByteArrayInputStream baIn) { // 更改类内 in 的读取对象为 输入字节流 baIn this.in = new Scanner(baIn); }
需要注意的时,该方法需要在类示例之后,具体方法被调用测试之前调用,即在开始测试待测试方法前设置好输入字节流。
public void ReadAndShow() { String line = in.nextLine(); System.out.println("This is the input: \n"+line); } public static void main(String[] args) { ReadFromConsole r = new ReadFromConsole(); // 输入字符串中的 \n 换行符将被 Scanner.nextline()方法识别 ByteArrayInputStream in1 = new ByteArrayInputStream("1\n2".getBytes()); r.setIn(in1); r.ReadAndShow(); r.ReadAndShow(); // 重新调用setIn方法以重新设置分配字符串 ByteArrayInputStream in2 = new ByteArrayInputStream("3".getBytes()); r.setIn(in2); r.ReadAndShow(); }
输出结果:
This is the input: 1 This is the input: 2 This is the input: 3
优点为可以在测试不同方法时灵活设置输入流的具体内容;
缺点为需要在被测试类内新增输入流设置方法,在测试完成后需要删除该方法并修改Scanner对象的限制符。
方案2
直接在测试类内重定向输入流,间接改变被调用函数输入流
private static ByteArrayInputStream in; /* * 2.直接重定向输入流 * System.setIn((ByteArrayInputStream in); * 即通过重定向,间接改变输入流 */ /** * 设置输入字符串作为输入流 * @param input - 测试所需输入字符串 */ public void setInput(String input) { in = new ByteArrayInputStream(input.getBytes()); System.setIn(in); } @Test public void testReadFromConsole() { // 换行符\n 以匹配多次 Scanner.nextline() String inputMessage = "one\n" + "two\n" + "three\n" + "four\n" + "five\n" + ""; // 仅设置一次输入流 setInput(inputMessage); // 直接调用需从控制台输入的方法,不需要调用待测类中的输入流设置方法 ReadFromConsole r = new ReadFromConsole(); r.ReadAndShow(); r.ReadAndShow(); r.ReadAndShow(); r.ReadAndShow(); r.ReadAndShow(); }
测试结果:
This is the input:
one
This is the input:
two
This is the input:
three
This is the input:
four
This is the input:
five
优点为不需要修改测试函数,直接在测试类中重定向标准输入流,待测类受到影响;
缺点为必须在待测类实例化后测试开始前一次性初始化所有输入字符串,以\n为分隔,在后续测试方法时不能在调用重定向标准输入流,否则会抛出异常。