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

时间:2010-11-19 23:51:38

标签: java command-line junit

所有

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

我正在尝试为我已经实现的每个功能编写JUnit测试用例,但是无法弄清楚如何将实现代码中的System.in重定向到我的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?

希望它有意义

5 个答案:

答案 0 :(得分:11)

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

public class Processor {
    void processInput(Reader r){ ... }
}

然后是两个实例:

Processor live = new Processor(new InputStreamReader(System.in));
Processor test = new Processor(new FileReader("C:/tmp/tests.txt");

习惯于对界面进行编码将为您的程序的几乎每个方面带来巨大的好处!

另请注意,Reader 在Java程序中处理基于字符的输入的惯用方法。应保留InputStream s用于原始字节级处理。

答案 1 :(得分:4)

答案 2 :(得分:3)

我建议你将代码分为三个部分:

  • 读取输入(如示例中的name
  • 执行您需要处理的输入
  • 打印结果

您不需要测试读取输入和打印结果,因为Java代码已经由编写Java的人员测试过。

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

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

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

@Test
public void testNameConsumer() {
    // Prepare inputs
    String name = "Jon";
    String result = nameConsumer(name);
    assertEquals("Doe", result);
}

将您的printlnreadLine来电转移到其他方法并使用nameConsumer左右,但不能用于单元测试。

在这里阅读更多相关信息:

保持简单,它会得到回报。

答案 3 :(得分:2)

System Rules提供了用于在JUnit测试中模拟输入的规则TextFromStandardInputStream

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

  @Test
  public void test() {
    systemInMock.provideText("name\nsomething else\n");
    YourApp.main();
    //assertSomething
  }
}

有关详细信息,请查看System Rules documentation

答案 4 :(得分:1)

这需要basic looping console application,并使用oxbow_lakes' answer中的提示使其可测试。

正确的课程:

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);
      }
   }
}

测试(JUnit4):

import static org.junit.Assert.assertEquals;
import static testableloopingconsoleapp.TestableLoopingConsoleExample.EXIT_COMMAND;
import static testableloopingconsoleapp.TestableLoopingConsoleExample.EXIT_RESPONSE;
import static testableloopingconsoleapp.TestableLoopingConsoleExample.INPUT_LINE_PREFIX;
import static testableloopingconsoleapp.TestableLoopingConsoleExample.RESPONSE_PLACEHOLDER; 

import org.junit.Before;
import org.junit.Test; 

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.StringReader; 

public class TestableLoopingConsoleExampleTest { 

  private final ByteArrayOutputStream out = new ByteArrayOutputStream();
  private final ByteArrayOutputStream err = new ByteArrayOutputStream(); 

  @Before
  public final void resetOutputStreams() {
     out.reset();
     err.reset();
  } 

...续...

  @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());
  } 

}