测试基于控制台的应用程序/程序 - Java
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4230402/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Testing console based applications/programs - Java
提问by name_masked
All,
全部,
I have written a PhoneBook application in Java that is command line based. The application basically asks for some details of user like Name, Age, Address and phone numbers and stores them in a file. Other operations involve looking up PhoneBook by name, phone number etc. All the details are entered through console.
我用 Java 编写了一个基于命令行的 PhoneBook 应用程序。该应用程序基本上会询问用户的一些详细信息,例如姓名、年龄、地址和电话号码,并将它们存储在一个文件中。其他操作包括按姓名、电话号码等查找PhoneBook。所有详细信息均通过控制台输入。
I am trying to write JUnit test cases for each of the functionalities that I have implemented but not able to figure out how to redirect System.in
in the implementation code to something in my JUnit test methods that would supply those values when my actual code stops for user input?
我正在尝试为我已实现的每个功能编写 JUnit 测试用例,但无法弄清楚如何将System.in
实现代码重定向到我的 JUnit 测试方法中的某些内容,当我的实际代码停止供用户输入时,这些内容将提供这些值?
Example:
例子:
My implementation code has:
我的实现代码有:
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?
Hope it makes sense
希望这是有道理的
采纳答案by oxbow_lakes
Why not write your application to take a Reader
as input? That way, you can easily replace an InputStreamReader(System.in)
with a FileReader(testFile)
为什么不编写您的应用程序以将 aReader
作为输入?这样,您可以轻松地将 an 替换InputStreamReader(System.in)
为FileReader(testFile)
public class Processor {
void processInput(Reader r){ ... }
}
And then two instances:
然后是两个实例:
Processor live = new Processor(new InputStreamReader(System.in));
Processor test = new Processor(new FileReader("C:/tmp/tests.txt");
Getting used to coding to an interface will bring great benefits in almost every aspect of your programs!
习惯于对界面进行编码将为您程序的几乎每个方面带来巨大的好处!
Note also that a Reader
is theidiomatic way to process character-based input in Java programs. InputStream
s should be reserved for raw byte-level processing.
另请注意, aReader
是在 Java 程序中处理基于字符的输入的惯用方法。InputStream
s 应保留用于原始字节级处理。
回答by thejh
回答by icyrock.com
I suggest you to separate the code into three parts:
我建议你将代码分成三部分:
- Read input (like
name
in your example) - Do what you need to do with that input
- Print the results
- 读取输入(如
name
您的示例) - 对该输入执行您需要执行的操作
- 打印结果
You do not need to test reading input and printing results, as that's Java code that is already tested by people writing Java.
您不需要测试读取输入和打印结果,因为这是已经由编写 Java 的人测试过的 Java 代码。
The only thing you need to test is the thing youare doing, whatever that is. Unit tests are named like that because they tests units of code in isolation. You don't test the whole program, you test small pieces that are self-contained and have a well-defined function.
你唯一需要测试的是你正在做的事情,不管那是什么。单元测试之所以如此命名是因为它们单独测试代码单元。您不测试整个程序,而是测试自包含并具有明确定义的功能的小块。
In unit tests, you should not rely on input/output operations. You should provide inputs and expected outputs directly in the unit test. It is sometimes convenient to use File reading operations to supply input or output (e.g. if the amount of data is huge), but as a general rule the more you go into input/output in your unit tests, the more complex they become and you are more likely not to do unit, but integration tests.
在单元测试中,您不应该依赖输入/输出操作。您应该直接在单元测试中提供输入和预期输出。有时使用文件读取操作来提供输入或输出很方便(例如,如果数据量很大),但作为一般规则,您在单元测试中进入输入/输出的次数越多,它们变得越复杂,您更有可能不做单元,而是做集成测试。
In your case, you use name
somehow. If that is the only parameter, then make a method - let's call it nameConsumer
- that takes that name, does something and returns its result. In your unit tests, do something like this:
在您的情况下,您name
以某种方式使用。如果这是唯一的参数,那么创建一个方法 - 让我们调用它nameConsumer
- 使用该名称,执行某些操作并返回其结果。在你的单元测试中,做这样的事情:
@Test
public void testNameConsumer() {
// Prepare inputs
String name = "Jon";
String result = nameConsumer(name);
assertEquals("Doe", result);
}
Move your println
and readLine
calls to other methods and use around nameConsumer
, but not in your unit tests.
将您的println
和readLine
调用移动到其他方法并使用 around nameConsumer
,但不在您的单元测试中。
Read more about this here:
在此处阅读更多相关信息:
- http://haacked.com/archive/2008/07/22/unit-test-boundaries.aspx
- C# example, but still: http://dotnet.dzone.com/news/unit-testing-file-io
- http://haacked.com/archive/2008/07/22/unit-test-boundaries.aspx
- C# 示例,但仍然是:http: //dotnet.dzone.com/news/unit-testing-file-io
Keep it simple, it pays off.
保持简单,它会得到回报。
回答by Stefan Birkner
The library System Rulesprovides the rule TextFromStandardInputStreamfor simulating input in JUnit tests.
库系统规则提供了规则TextFromStandardInputStream用于模拟 JUnit 测试中的输入。
public class YourAppTest {
@Rule
public TextFromStandardInputStream systemInMock = emptyStandardInputStream();
@Test
public void test() {
systemInMock.provideText("name\nsomething else\n");
YourApp.main();
//assertSomething
}
}
For details have a look at the System Rules documentation.
有关详细信息,请查看系统规则文档。
回答by aliteralmind
This takes a basic looping console applicationand makes it testable, using the ideas from oxbow_lakes' answer.
这需要一个基本的循环控制台应用程序,并使用oxbow_lakes' answer 中的想法使其可测试。
The class-proper:
类正确的:
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());
}
}
...continued...
...继续...
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);
}
}
}
The test (JUnit4):
测试(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();
}
...continued...
...继续...
@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());
}
}