围绕Sytem.in和System.out进行JUnit测试

Ale*_*dre 2 java junit

我被要求在从命令行运行和操作的旧Java应用程序中引入单元测试.基本上主循环打印出一个菜单,用户输入一些内容并显示更多数据.

此Main类说明了应用程序的工作原理.

public class Main{

    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    public static void main(String argv[]) throws IOException{
        while (true) {
            char input = (char) reader.read();

            if(input == 'x'){
                return;
            }

            System.out.println(input);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望我的测试方法看起来像这样

public void testCaseOne(){
    Main.main();
    String result = "";

    result = sendInput("1");
    assertEqual(result, "1");

    result = sendInput("x");
    assertEqual(result,"");
}
Run Code Online (Sandbox Code Playgroud)

我知道System.setOut()System.setIn()方法,但我无法找到一种方法使该System.setIn()方法在此上下文中工作,因为该reader.read()方法阻止了我的线程.

我的测试设计错了吗?有没有办法设计sendInput()方法来通过阻塞reader.read()调用?

Jef*_*rey 6

我建议重构代码以允许注入输入/输出流,然后你可以模拟它们.如果你想把它变成类似的东西

public class Main{

    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    public static void main(String argv[]) throws IOException{
        new YourClass(reader,System.out).run();
    }
}

public class YourClass { // I don't know what your class is actually doing, but name it something appropriate
  private final InputReader reader;
  private final PrintStream output;

  public YourClass(InputReader reader, PrintStream output) {
       this.reader = reader;
       this.output = ouptut;
  }

  public void run() {

        while (true) {
        char input = (char) reader.read();

        if(input == 'x')
            return;

        output.println(input);
  }
}
Run Code Online (Sandbox Code Playgroud)

这个设计做了两件事:

  1. 它需要你的主类逻辑.通常,main方法实际上仅用于启动应用程序.

  2. YourClass更容易进行单元测试.在测试中,您可以简单地模拟输入/输出.

编辑:更新此重构如何帮助阻止IO问题

通过如上所示使读取器/输出可注入,您实际上不需要使用真正的System.in和System.out - 您可以使用模拟.这消除了实际具有阻塞读取的需要.

public void testCaseOne(){
    // pseudocode for the mock - this will vary depending on your mock framework
    InputReader reader = createMock(InputReader);
    // the first time you read it will be a "1", the next time it will be an "x"
    expect(reader.read()).andReturn("1");
    expect(reader.read()).andReturn("x");

    PrintStream stream = createMock(PrintStream);
    // only expect the "1" to get written. the "x" is the exit signal
    expect(stream.println("1"));

    new YourClass(reader,stream).run();
    verifyMocks();
}
Run Code Online (Sandbox Code Playgroud)