我试图通过控制台模拟多个用户输入,我在阅读'> 1'时遇到问题作为输入。只阅读' 1'似乎很好。
考虑以下方法进行测试:
public int getMainMenuSelectedOption() {
Scanner scan = new Scanner(System.in);
int i = scan.nextInt();
while ((i < 1) || (i > 5)) {
System.out.println("Invalid option, please select again");
i = scan.nextInt();
}
return i;
}
我这样写了测试
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
@Before
public void setUpStreams() {
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
}
@Test
public void testGetMainMenuSelectedOptionNotifyInvalidOption() {
ByteArrayInputStream in = new ByteArrayInputStream("0".getBytes());
System.setIn(in);
app.getMainMenuSelectedOption();
assertEquals("Invalid option, please select again", outContent.toString());
ByteArrayInputStream in2 = new ByteArrayInputStream("1".getBytes());
System.setIn(in2);
}
// Also tried with SequenceInputStream. This will not get the expected outstream of "Invalid option".
@Test
public void testGetMainMenuSelectedOptionNotifyInvalidOption() {
ByteArrayInputStream in1 = new ByteArrayInputStream("0".getBytes());
ByteArrayInputStream in2 = new ByteArrayInputStream("1".getBytes());
SequenceInputStream in = new SequenceInputStream(in1,in2);
System.setIn(in);
app.getMainMenuSelectedOption();
assertEquals("Invalid option, please select again\n", outContent.toString());
}
// This test runs fine
@Test
public void testGetMainMenuSelectedValidOption() {
ByteArrayInputStream in = new ByteArrayInputStream("1".getBytes());
System.setIn(in);
app.getMainMenuSelectedOption();
}
我收到以下错误
java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:862)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at com.twu.biblioteca.BibliotecaApp.getMainMenuSelectedOption(BibliotecaApp.java:42)
at com.twu.biblioteca.ExampleTest.testGetMainMenuSelectedOptionNotifyInvalidOption(ExampleTest.java:73)
答案 0 :(得分:3)
在你的方法中:
Scanner scan = new Scanner(System.in);
int i = scan.nextInt();
while ((i < 1) || (i > 5)) {
System.out.println("Invalid option, please select again");
i = scan.nextInt();
}
return i;
输入0
的将元素0
读取到i
,然后进入循环(因为条件为i<1
),然后打印一条消息,然后尝试扫描循环中的下一个元素。因为输入流已经耗尽
根据{{3}}:
<强>抛出:强>
NoSuchElementException - 如果输入用尽
在我看来,一切都很好(除了方法逻辑中的错误)。
<强> ---------- 编辑 ---------- 强>
有效输入应为1到4,包括两者。为什么输入 流累了?
我不知道为什么你不使用调试器来检查你的代码中发生了什么,只需点击IDE右边栏中的某行来设置断点,然后右键单击你的测试方法并选择& #34;调试&#34;从上下文菜单中。
无论如何,让我们在纸上逐步调试输入0
的方法:
1 Scanner scan = new Scanner(System.in);
2 int i = scan.nextInt();
3 while ((i < 1) || (i > 5)) {
4 System.out.println("Invalid option, please select again");
5 i = scan.nextInt();
6 }
7 return i;
输入缓冲区包含单个字符0
。
0
到变量i
。变量i
现在包含0
值。在此之后输入流为空(已耗尽)((i < 1) || (i > 5))
。由于i
等于0
,条件为真,程序流进入循环我认为现在应该很清楚。为什么输入流耗尽?因为它只有一个元素0
在第2行被引用,所以在第5行它是空的(耗尽)
您的代码有错误 - 但这就是我们进行单元测试以查找此类错误的原因)。
提示:使用the documentation of Scanner#nextInt()方法测试输入流中是否存在下一个int以避免异常。
答案 1 :(得分:1)
我建议你采用一种处理测试的新方法。
实施您自己的ByteArrayInputStream
- &gt;只需将现有的ByteArrayInputStream扩展到您自己的MyTestByteArrayInputStream中。
考虑以下示例:
import java.io.ByteArrayInputStream;
import java.util.stream.*;
public class MyClass {
public static class MyTestByteArrayInputStream extends ByteArrayInputStream {
private Byte mockedBuffer = null;
public MyTestByteArrayInputStream(byte b) {
super(new byte[] {b});
this.mockedBuffer = b;
}
public MyTestByteArrayInputStream(byte[] in) {
super(in);
}
public MyTestByteArrayInputStream(byte[] in, int offset, int length) {
super(in,offset,length);
}
@Override
public int read() {
if(mockedBuffer == null) {
return super.read();
}
else {
return mockedBuffer;
}
}
}
public static void main(String args[]) {
byte b = 49;
MyTestByteArrayInputStream myTestInputStream = new MyTestByteArrayInputStream(b);
IntStream.range(0,10).forEach(i -> {
System.out.println("Invoking #" + i +": " + myTestInputStream.read());
});
}
}
输出:
Invoking #0: 49
Invoking #1: 49
Invoking #2: 49
Invoking #3: 49
Invoking #4: 49
Invoking #5: 49
Invoking #6: 49
Invoking #7: 49
Invoking #8: 49
Invoking #9: 49
你的模拟实现有很多功能。
在我的示例中,我添加了一个构造函数,它允许您使用单个字节实例化IS,稍后这将是read()重写方法的唯一输出。
这意味着您无需提供&#39;带输入的输入流 - 调用read()方法永远不会导致NoSuchElementException。
我还高度重申你使用一个实际的模拟框架:JMock和Mockito是非常了解和有用的工具,广泛用于单元测试。
使用模拟框架可以节省实际实现MyTestByteArrayInputStream的需要 - 而只是简单地覆盖特定ByteArrayInputStream实例的行为。
希望这有助于并随意询问/评论此解决方案。
答案 2 :(得分:0)
我已经想过,要设置顺序控制台输入流,只需添加&#34; \ n&#34;作为换行。
例如,我想要一个&#39; 0&#39;其次是&#39; 1&#39;控制台输入,所以以下工作
ByteArrayInputStream in = new ByteArrayInputStream("0\n1\n".getBytes());
System.setIn(in);
app.getMainMenuSelectedOption();
assertEquals("Invalid option, please select again\n", outContent.toString());