2010-11-19 93 views
9

全部,基於測試控制檯的應用程序/程序 - Java

我已經在基於命令行的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? 

希望這是有道理的

+0

你可以找到一些示例代碼來做你在這裏描述的內容:http://illegalargumentexception.blogspot.com/2010/09/java-systemconsole-ides-and-testing.html但它主要是一種實現oxbow_lakes在他的回答中描述。 – McDowell 2010-11-20 10:42:27

回答

11

爲什麼不寫你的應用程序採取Reader作爲輸入?這樣一來,你可以很容易地用FileReader(testFile)

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

然後兩個實例替換InputStreamReader(System.in)

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

習慣編碼到一個接口會在你的程序的幾乎所有方面帶來極大的好處!

還請注意,Reader處理Java程序中基於字符的輸入的慣用方式。應該爲原始字節級處理保留InputStream

+0

我不理解你。上面的代碼如何在我的示例中工作?所以如果我必須在我的測試方法中傳遞命令行值,我必須使用文件'tests.txt'?如果是這樣的話,我希望通過我的測試方法傳遞值,而不是通過創建文件來傳遞值。 – 2010-11-20 00:06:52

+0

這兩個'Reader'具有相同的接口。你爲單元測試使用'FileReader',因爲它不需要用戶輸入(爲了測試不同的輸入而準備文本文件)。您在執行程序時使用'InputStreamReader(System.in)',因此它仍然會直接要求用戶輸入。 – Santa 2010-11-20 00:11:11

3
+0

你能否詳細說明你的答案?爲什麼我會使用文件「input.txt」? – 2010-11-20 00:09:01

+1

@ darkie15:這只是一個例子。 – thejh 2010-11-20 11:31:10

+0

@ darkie15:你也可以使用'PipedInputStream'和'PipedOutputStream'。 – thejh 2010-11-20 12:10:03

3

我建議你到代碼分成三個部分:

  • 讀取輸入(如在你的例子name
  • 你需要做的是輸入
  • 打印的結果是什麼

您不需要測試讀取輸入和打印結果,因爲這是已由編寫Java的人測試過的Java代碼。

你需要測試的唯一東西是在做什麼,不管是什麼。單元測試就是這樣命名的,因爲它們單獨測試代碼單元。你不測試整個程序,你測試的小塊是獨立的,並有一個明確的功能。

在單元測試中,您不應該依賴輸入/輸出操作。您應該在單元測試中直接提供輸入和預期輸出。使用文件讀取操作來提供輸入或輸出(例如,如果數據量很大)有時很方便,但作爲一般規則,單元測試中輸入/輸出越多,它們變得越複雜更有可能不是單位,而是整合測試。

在你的情況下,你以某種方式使用name。如果這是唯一的參數,那麼創建一個方法 - 我們稱之爲nameConsumer - 接受該名稱,執行某些操作並返回結果。在單元測試中,做這樣的事情:

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

移動你的printlnreadLine調用其他方法和在單元測試中使用周圍nameConsumer,但不是。

瞭解更多關於此這裏:

保持簡單,付出總有回報。

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

+0

非常非常有用的圖書館!在這裏使用它:https://github.com/binwiederhier/syncany/blob/7ee6174877a646c78e2f5faa36115d01bf6cc9ec/syncany-cli/src/test/java/org/syncany/tests/cli/InitCommandTest.java#L137 – binwiederhier 2014-03-15 19:43:54

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

} 
+0

Phwoaaa .... that不如一些測試可以。 Upvoted tho,thx! – Crowie 2017-03-28 01:52:55