如何使用 Scanner 和 while 循环对 void 方法进行 JUnit 测试
How to make JUnit test for void method with Scanner and while loop
我想使用参数 [-1,0,24,2,32] 测试方法 setDay()。
我知道扫描仪reader可以
String test="-1 0 24 2 32";
Scanner reader=new Scanner(test);
主要问题是无限循环和无效方法。我们如何测试这种代码?这是示例代码:
public NameOfTheDay(){
int day=1;
}
{...}
public void setDay(Scanner reader) {
while (true) {
System.out.print("Day: ");
String input = reader.nextLine();
if (input.matches("\d{2}")) {
int day = Integer.parseInt(input);
if (day > 0 && day < 32) {
this.day = day;
return;
}
}
System.out.println("Wrong day. Try again.");
}
}
感谢您的回答。
我不会关注您的方法的内容,而只会关注如何对期望 Scanner 对象作为参数的方法进行单元测试的问题。
简单的答案是:将您的测试输入数据作为字符串提供,并围绕它构建一个扫描器,如下所示:
@Test
public void testSetDay_positive() {
String testInput = "23\n";
Scanner testScanner = new Scanner(testInput);
NameOfTheDay notd = new NameOfTheDay();
notd.setDay(testScanner);
Assert.assertEquals(23, notd.getDay()); // or whatever condition to test
}
现在越来越难了。也许您想先测试无效输入,然后确保使用第二个输入?
@Test
public void testSetDay_negative_then_positive() {
String testInput = "999\n23\n"; // two lines of input here
Scanner testScanner = new Scanner(testInput);
NameOfTheDay notd = new NameOfTheDay();
notd.setDay(testScanner);
Assert.assertEquals(23, notd.getDay()); // or whatever condition to test
}
如果您想测试错误消息是否写入 System.out
,您必须将其替换为自定义流以便之后进行测试:
ByteArrayOutputStream mockOut = new ByteArrayOutputStream();
PrintStream newOut = new PrintStream(mockOut);
System.setOut(newOut);
// execute test from above
Assert.assertTrue(new String(mockOut.toByteArray(), "UTF-8").contains("Wrong day. Try again."));
不过,大多数对您问题的评论都包含有价值的输入(将验证移至额外的方法等),应该予以考虑。
How can we test this kind of code?
你不能。
unittest 验证被测代码的 public 可观察行为,其中 "public observable behavior" 是任何 rreturn 值 或 与依赖项的通信。
使用测试替身检查与依赖项的通信,我们(通常)使用模拟框架创建我们注入到被测代码中。
一个主要的先决条件是您的代码干净利落地包含单一Responsibility/Separation 关注点 模式。
您的代码没有 return 任何内容,也无法替换感兴趣的依赖项(此处 System.out
),因为它混合了业务逻辑与用户交互。
有些人可能会争辩说,您可以将测试替身分配给 System.out
或使用 PowerMock 来替换依赖项,但恕我直言,这只是对您糟糕设计的投降,不会随着程序的增长而得到回报。
我想使用参数 [-1,0,24,2,32] 测试方法 setDay()。
我知道扫描仪reader可以
String test="-1 0 24 2 32";
Scanner reader=new Scanner(test);
主要问题是无限循环和无效方法。我们如何测试这种代码?这是示例代码:
public NameOfTheDay(){
int day=1;
}
{...}
public void setDay(Scanner reader) {
while (true) {
System.out.print("Day: ");
String input = reader.nextLine();
if (input.matches("\d{2}")) {
int day = Integer.parseInt(input);
if (day > 0 && day < 32) {
this.day = day;
return;
}
}
System.out.println("Wrong day. Try again.");
}
}
感谢您的回答。
我不会关注您的方法的内容,而只会关注如何对期望 Scanner 对象作为参数的方法进行单元测试的问题。
简单的答案是:将您的测试输入数据作为字符串提供,并围绕它构建一个扫描器,如下所示:
@Test
public void testSetDay_positive() {
String testInput = "23\n";
Scanner testScanner = new Scanner(testInput);
NameOfTheDay notd = new NameOfTheDay();
notd.setDay(testScanner);
Assert.assertEquals(23, notd.getDay()); // or whatever condition to test
}
现在越来越难了。也许您想先测试无效输入,然后确保使用第二个输入?
@Test
public void testSetDay_negative_then_positive() {
String testInput = "999\n23\n"; // two lines of input here
Scanner testScanner = new Scanner(testInput);
NameOfTheDay notd = new NameOfTheDay();
notd.setDay(testScanner);
Assert.assertEquals(23, notd.getDay()); // or whatever condition to test
}
如果您想测试错误消息是否写入 System.out
,您必须将其替换为自定义流以便之后进行测试:
ByteArrayOutputStream mockOut = new ByteArrayOutputStream();
PrintStream newOut = new PrintStream(mockOut);
System.setOut(newOut);
// execute test from above
Assert.assertTrue(new String(mockOut.toByteArray(), "UTF-8").contains("Wrong day. Try again."));
不过,大多数对您问题的评论都包含有价值的输入(将验证移至额外的方法等),应该予以考虑。
How can we test this kind of code?
你不能。
unittest 验证被测代码的 public 可观察行为,其中 "public observable behavior" 是任何 rreturn 值 或 与依赖项的通信。
使用测试替身检查与依赖项的通信,我们(通常)使用模拟框架创建我们注入到被测代码中。
一个主要的先决条件是您的代码干净利落地包含单一Responsibility/Separation 关注点 模式。
您的代码没有 return 任何内容,也无法替换感兴趣的依赖项(此处 System.out
),因为它混合了业务逻辑与用户交互。
有些人可能会争辩说,您可以将测试替身分配给 System.out
或使用 PowerMock 来替换依赖项,但恕我直言,这只是对您糟糕设计的投降,不会随着程序的增长而得到回报。