基于测试控制台的应用程序/程序 - 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?
希望这是有道理的
为什么不写你的应用程序采取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
。
我不理解你。上面的代码如何在我的示例中工作?所以如果我必须在我的测试方法中传递命令行值,我必须使用文件'tests.txt'?如果是这样的话,我希望通过我的测试方法传递值,而不是通过创建文件来传递值。 – 2010-11-20 00:06:52
这两个'Reader'具有相同的接口。你为单元测试使用'FileReader',因为它不需要用户输入(为了测试不同的输入而准备文本文件)。您在执行程序时使用'InputStreamReader(System.in)',因此它仍然会直接要求用户输入。 – Santa 2010-11-20 00:11:11
我建议你到代码分成三个部分:
- 读取输入(如在你的例子
name
) - 你需要做的是输入
- 打印的结果是什么
您不需要测试读取输入和打印结果,因为这是已由编写Java的人测试过的Java代码。
你需要测试的唯一东西是你在做什么,不管是什么。单元测试就是这样命名的,因为它们单独测试代码单元。你不测试整个程序,你测试的小块是独立的,并有一个明确的功能。
在单元测试中,您不应该依赖输入/输出操作。您应该在单元测试中直接提供输入和预期输出。使用文件读取操作来提供输入或输出(例如,如果数据量很大)有时很方便,但作为一般规则,单元测试中输入/输出越多,它们变得越复杂更有可能不是单位,而是整合测试。
在你的情况下,你以某种方式使用name
。如果这是唯一的参数,那么创建一个方法 - 我们称之为nameConsumer
- 接受该名称,执行某些操作并返回结果。在单元测试中,做这样的事情:
@Test
public void testNameConsumer() {
// Prepare inputs
String name = "Jon";
String result = nameConsumer(name);
assertEquals("Doe", result);
}
移动你的println
和readLine
调用其他方法和在单元测试中使用周围nameConsumer
,但不是。
了解更多关于此这里:
- http://haacked.com/archive/2008/07/22/unit-test-boundaries.aspx
- C#示例,但仍:http://dotnet.dzone.com/news/unit-testing-file-io
保持简单,付出总有回报。
库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
}
}
非常非常有用的图书馆!在这里使用它: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
这需要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());
}
}
Phwoaaa .... that不如一些测试可以。 Upvoted tho,thx! – Crowie 2017-03-28 01:52:55
你可以找到一些示例代码来做你在这里描述的内容:http://illegalargumentexception.blogspot.com/2010/09/java-systemconsole-ides-and-testing.html但它主要是一种实现oxbow_lakes在他的回答中描述。 – McDowell 2010-11-20 10:42:27