【问题标题】:How to make JUnit test for void method with Scanner and while loop [duplicate]如何使用 Scanner 和 while 循环对 void 方法进行 JUnit 测试 [重复]
【发布时间】:2023-03-29 00:48:01
【问题描述】:

我想使用参数 [-1,0,24,2,32] 测试方法 setDay()。

我知道扫描仪阅读器可以

String test="-1 0 24 2 32"; Scanner reader=new Scanner(test);

主要问题是无限循环和void方法。我们如何测试这种代码?这是示例代码:

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.");
    }
}

感谢您的回答。

【问题讨论】:

  • 您的代码完全是错误的,对于输入“ab”,该方法将永远不会返回。
  • String input = reader.nextLine(); 应该在 while 循环中
  • 我打算在输入为数字时返回。但这不是我的问题。我想知道如何检查此方法。我搜索类似的主题,但从未得到正确的答案。 @Oneiros 对。已编辑
  • 在我看来这是一个非常奇怪的单元测试。为什么要测试 java 扫描仪的功能?单元测试不应该需要用户输入,你不是在其他测试中按程序执行此代码吗?我只测试日期整数的条件......我弄错了吗?
  • 你可以测试这个的唯一方法是通过模拟系统控制台(因为这是你的方法传达故障的唯一方法)。我强烈建议您以任何其他不需要这样做的方式进行。

标签: java unit-testing while-loop void junit5


【解决方案1】:

我们如何测试这种代码?

你不能。

unittest 验证被测代码的公共可观察行为,其中“公共可观察行为”是任何 r返回值与依赖项的通信 .

使用 test doubles 检查与依赖项的通信,我们(通常)使用 mocking 框架 创建它,并将其 注入 到下面的代码中测试。

一个主要的预先要求是您的代码干净地结合了单一职责/关注点分离模式。

您的代码不会返回任何内容,也无法替换感兴趣的依赖项(此处为 System.out),因为它将业务逻辑与用户交互混合在一起。


有些人可能会争辩说,您可以将测试替身分配给 System.out 或使用 PowerMock 来替换依赖项,但恕我直言,这只是对您糟糕设计的投降,不会因为您的程序而得到回报成长。

【讨论】:

  • 单元测试不是唯一的测试。还有集成测试,实时系统中的端到端测试等。说“你不能测试这个”是不正确的。可以,但不能通过单元测试。
  • 我不同意。该方法更改了this.day,因此存在除了 System.out 输出之外的可观察行为。
  • 还有一种方法可以设置System.out,只需调用System.setOut()即可。
  • @FlorianAlbrecht “方法改变了this.day,所以除了System.out输出之外还有可观察的行为” this.day内部状态的一部分 的剪切,不应在 UnitTesting 中使用。
  • @M.Prokhorov 直接使用 全局变量System.out 肯定是)不是一个好主意,尤其是在测试时...
【解决方案2】:

我不会关注您的方法的内容,而只是关注如何对期望 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."));

不过,您问题的大多数 cmets 都包含有价值的输入(将验证移至额外的方法等),应予以考虑。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-05
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2015-05-11
    • 1970-01-01
    相关资源
    最近更新 更多