测试基于控制台的应用程序/程序-Java

测试基于控制台的应用程序/程序-Java,java,command-line,junit,Java,Command Line,Junit,全部, 我用Java编写了一个基于命令行的电话簿应用程序。该应用程序基本上要求用户提供一些详细信息,如姓名、年龄、地址和电话号码,并将其存储在文件中。其他操作包括按姓名、电话号码等查找电话簿。所有详细信息都通过控制台输入 我正在尝试为我已经实现的每个功能编写JUnit测试用例,但无法找出如何将实现代码中的系统重定向到JUnit测试方法中的某个内容,当我的实际代码停止供用户输入时,该方法将提供这些值 例如: 我的实现代码有: BufferedReader is = new BufferedRead

全部,

我用Java编写了一个基于命令行的电话簿应用程序。该应用程序基本上要求用户提供一些详细信息,如姓名、年龄、地址和电话号码,并将其存储在文件中。其他操作包括按姓名、电话号码等查找电话簿。所有详细信息都通过控制台输入

我正在尝试为我已经实现的每个功能编写JUnit测试用例,但无法找出如何将实现代码中的
系统重定向到JUnit测试方法中的某个内容,当我的实际代码停止供用户输入时,该方法将提供这些值

例如:

我的实现代码有:

BufferedReader is = new BufferedReader (new InputStreamReader(System.in));
System.out.println("Please enter your name:");
String name = is.readLine();             // My test cases stop at this line. How can I pass command line values i.e. redirect System.in to my test based values?

希望它有意义

为什么不编写应用程序,将
读取器
作为输入?这样,您就可以轻松地将
InputStreamReader(System.in)
替换为
FileReader(testFile)

然后是两个例子:

Processor live = new Processor(new InputStreamReader(System.in));
Processor test = new Processor(new FileReader("C:/tmp/tests.txt");
习惯于对界面进行编码将在程序的几乎每个方面带来巨大的好处


还要注意,
读取器
是Java程序中处理基于字符输入的惯用方法<代码>输入流
s应保留用于原始字节级处理。

(新建))

我建议您将代码分为三部分:

  • 读取输入(如示例中的
    name
  • 用这些输入做你需要做的事情
  • 打印结果
您不需要测试读取输入和打印结果,因为编写Java的人已经测试了Java代码

你唯一需要测试的就是你正在做的事情,不管是什么。单元测试是这样命名的,因为它们单独测试代码单元。您不测试整个程序,而是测试自包含的、具有定义良好的功能的小块

在单元测试中,您不应该依赖输入/输出操作。您应该在单元测试中直接提供输入和预期输出。有时,使用文件读取操作来提供输入或输出(例如,如果数据量很大)是很方便的,但一般来说,在单元测试中,输入/输出越多,它们就变得越复杂,并且您更可能不进行单元测试,而进行集成测试

在您的情况下,您会以某种方式使用
name
。如果这是唯一的参数,那么创建一个方法——我们称之为
nameConsumer
——使用该名称,执行某些操作并返回其结果。在单元测试中,执行以下操作:

@Test
public void testNameConsumer() {
    // Prepare inputs
    String name = "Jon";
    String result = nameConsumer(name);
    assertEquals("Doe", result);
}
将您的
println
readLine
调用移动到其他方法,并在
namecuster
周围使用,但不要在单元测试中使用

public class YourAppTest {
  @Rule
  public TextFromStandardInputStream systemInMock = emptyStandardInputStream();

  @Test
  public void test() {
    systemInMock.provideText("name\nsomething else\n");
    YourApp.main();
    //assertSomething
  }
}
请在此处阅读更多信息:

  • 例如,但仍然:
保持简单,就会有回报。

该库提供了在JUnit测试中模拟输入的规则

public class YourAppTest {
  @Rule
  public TextFromStandardInputStream systemInMock = emptyStandardInputStream();

  @Test
  public void test() {
    systemInMock.provideText("name\nsomething else\n");
    YourApp.main();
    //assertSomething
  }
}
有关详细信息,请查看。

这需要一个测试,并使用来自的想法使其可测试

班级本身:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;

public class TestableLoopingConsoleExample {

   public static final String INPUT_LINE_PREFIX = "> ";
   public static final String EXIT_COMMAND = "exit";
   public static final String RESPONSE_PLACEHOLDER = "...response goes here...";
   public static final String EXIT_RESPONSE = "Exiting.";

   public static void main(String[] cmdLineParams_ignored) throws IOException {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintStream out = new PrintStream(System.out);
      PrintStream err = new PrintStream(System.err);

      try {
         new TestableLoopingConsoleExample().main(cmdLineParams_ignored, in, out);
      } catch (Exception e) {  //For real use, catch only the exactly expected types
         err.println(e.toString());
      }
   }
……继续

   public void main(String[] cmdLineParams_ignored, BufferedReader in, PrintStream out)
         throws IOException {

      System.out.println("Enter some text, or '" + EXIT_COMMAND + "' to quit");

      while (true) {

         out.print(INPUT_LINE_PREFIX);
         String input = in.readLine();
         out.println(input);

         if (input.length() == EXIT_COMMAND.length() &&
            input.toLowerCase().equals(EXIT_COMMAND)) {

            out.println(EXIT_RESPONSE);
            return;
         }

         out.println(RESPONSE_PLACEHOLDER);
      }
   }
}
  @Test
  public void testableMain_validInputFromString_outputAsExpected() throws Exception {
     String line1 = "input line 1\n";
     String line2 = "input line 2\n";
     String line3 = "input line 3\n";
     String exitLine = EXIT_COMMAND + "\n"; 

     BufferedReader in = new BufferedReader(new StringReader(
         line1 + line2 + line3 + exitLine
     ));
     String expectedOutput =
         INPUT_LINE_PREFIX + line1 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + line2 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + line3 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + exitLine +
         EXIT_RESPONSE + "\n"; 

     String[] ignoredCommandLineParams = null; 

     new TestableLoopingConsoleExample().main(ignoredCommandLineParams, in, new PrintStream(out)); 

     assertEquals(expectedOutput, out.toString());
  } 

}
测试(JUnit4):

……继续

   public void main(String[] cmdLineParams_ignored, BufferedReader in, PrintStream out)
         throws IOException {

      System.out.println("Enter some text, or '" + EXIT_COMMAND + "' to quit");

      while (true) {

         out.print(INPUT_LINE_PREFIX);
         String input = in.readLine();
         out.println(input);

         if (input.length() == EXIT_COMMAND.length() &&
            input.toLowerCase().equals(EXIT_COMMAND)) {

            out.println(EXIT_RESPONSE);
            return;
         }

         out.println(RESPONSE_PLACEHOLDER);
      }
   }
}
  @Test
  public void testableMain_validInputFromString_outputAsExpected() throws Exception {
     String line1 = "input line 1\n";
     String line2 = "input line 2\n";
     String line3 = "input line 3\n";
     String exitLine = EXIT_COMMAND + "\n"; 

     BufferedReader in = new BufferedReader(new StringReader(
         line1 + line2 + line3 + exitLine
     ));
     String expectedOutput =
         INPUT_LINE_PREFIX + line1 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + line2 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + line3 +
         RESPONSE_PLACEHOLDER + "\n" +
         INPUT_LINE_PREFIX + exitLine +
         EXIT_RESPONSE + "\n"; 

     String[] ignoredCommandLineParams = null; 

     new TestableLoopingConsoleExample().main(ignoredCommandLineParams, in, new PrintStream(out)); 

     assertEquals(expectedOutput, out.toString());
  } 

}

我不明白你的意思。在我的示例中,上面发布的代码是如何工作的?因此,如果我必须在测试方法中传递命令行值,我必须使用文件
tests.txt
?如果是这样,我希望通过我的测试方法传递值,而不是创建文件来传递值。这两个
读取器具有相同的接口。单元测试使用
FileReader
,因为它不需要用户输入(准备文本文件是为了测试不同的输入)。您使用
InputStreamReader(System.in)
进行实时执行程序,因此它仍然会直接请求用户输入。请详细说明您的答案好吗?为什么我要使用文件“input.txt”?@darkie15:这只是一个例子。@darkie15:你也可以使用
PipedInputStream
PipedOutputStream
等等。你可以找到一些示例代码来实现你在这里描述的内容:但这基本上只是一种实现oxbow_lakes在回答中描述的内容的方法。非常有用的库!在这里使用它: