如何使用 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 来替换依赖项,但恕我直言,这只是对您糟糕设计的投降,不会随着程序的增长而得到回报。