这与这个问题不同:JUnit: How to simulate System.in testing?,这是关于模仿stdin。
我想知道的是如何测试(如在TDD中)使用main
方法的简单Java类等待输入。
我的测试:
@Test
public void appRunShouldWaitForInput(){
long startMillis = System.currentTimeMillis();
// NB obviously you'd want to run this next line in a separate thread with some sort of timeout mechanism...
// that's an implementation detail I've omitted for the sake of avoiding clutter!
App.main( null );
long endMillis = System.currentTimeMillis();
assertThat( endMillis - startMillis ).isGreaterThan( 1000L );
}
我的SUT main
:
public static void main(String args[]) {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter something : ");
String input = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
...测试失败。代码不等待。但是当你在命令提示符下运行应用程序时,确实会等待。
我的方式也尝试将stdin
设置为sthg else:
System.setIn(new ByteArrayInputStream( dummy.getBytes()));
scanner = new Scanner(System.in);
......这没有阻碍测试。
答案 0 :(得分:1)
作为更一般的规则,静态方法(例如'utf8' codec can't decode byte 0x88 in position 21: invalid start byte
方法)很难测试。因此,您几乎从不从测试代码中调用main
方法(或任何其他静态方法)。解决这个问题的一个常见模式是转换它:
main
到此:
public class App {
public static void main(String args[]) {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter something : ");
String input = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
现在你的测试变成了这个:
public class App {
private final InputStream input;
private final OutputStream output;
public App(InputStream input, OutputStream output) {
this.input = input;
this.output = output;
}
public static void main(String[] args) {
new App(System.in, System.out).start();
}
public void start() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(input));
output.print("Enter something : ");
String nextInput = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
关键的想法是,当您通过@Test
public void appRunShouldWaitForInput(){
ByteArrayOutputStream output = new ByteArrayOutputStream();
// As you have already noted, you would need to kick this off on another thread and use a blocking implementation of InputStream to test what you want to test.
new App(new ByteArrayInputStream(), output).start();
assertThat(output.toByteArray().length, is(0));
}
方法“实现”运行应用程序时,它将使用标准输入和输出流。但是,当您从测试中运行它时,它使用纯粹的内存输入/输出流,您可以在测试中完全控制它。 ByteArrayOutputStream只是一个示例,但您可以在我的示例中看到测试能够检查已写入输出流的实际字节。
答案 1 :(得分:1)
一般情况下:你无法测试你的程序是否在等到事情发生(因为如果它永远等待你就无法测试)
在这种情况下通常会做什么:
在你的情况下:不要测试它。 readLine
由经过相当密集测试的外部库提供。测试成本远高于此类测试的价值。重构并测试您的业务代码(字符串/流操作),而不是基础结构(系统输入)
在一般情况下,它测试并发编程。它很难,所以人们通常会尝试找到一些有用的简化: