使用模拟用户输入进行 JUnit 测试

2022-08-31 11:09:29

我正在尝试为需要用户输入的方法创建一些JUnit测试。所测试的方法看起来有点像下面的方法:

public static int testUserInput() {
    Scanner keyboard = new Scanner(System.in);
    System.out.println("Give a number between 1 and 10");
    int input = keyboard.nextInt();

    while (input < 1 || input > 10) {
        System.out.println("Wrong number, try again.");
        input = keyboard.nextInt();
    }

    return input;
}

有没有一种可能的方法可以自动将程序传递一个int,而不是我或其他人在JUnit测试方法中手动执行此操作?喜欢模拟用户输入?

提前致谢。


答案 1

您可以通过调用 System.setIn(InputStream in) 将 System.in 替换为您自己的流。InputStream 可以是一个字节数组:

InputStream sysInBackup = System.in; // backup System.in to restore it later
ByteArrayInputStream in = new ByteArrayInputStream("My string".getBytes());
System.setIn(in);

// do your thing

// optionally, reset System.in to its original
System.setIn(sysInBackup);

不同的方法可以通过将IN和OUT作为参数传递来使此方法更具可测试性:

public static int testUserInput(InputStream in,PrintStream out) {
    Scanner keyboard = new Scanner(in);
    out.println("Give a number between 1 and 10");
    int input = keyboard.nextInt();

    while (input < 1 || input > 10) {
        out.println("Wrong number, try again.");
        input = keyboard.nextInt();
    }

    return input;
}

答案 2

若要测试代码,应为系统输入/输出函数创建一个包装器。您可以使用依赖关系注入来执行此操作,从而为我们提供一个可以请求新整数的类:

public static class IntegerAsker {
    private final Scanner scanner;
    private final PrintStream out;

    public IntegerAsker(InputStream in, PrintStream out) {
        scanner = new Scanner(in);
        this.out = out;
    }

    public int ask(String message) {
        out.println(message);
        return scanner.nextInt();
    }
}

然后,您可以使用模拟框架(我使用Mockito)为您的函数创建测试:

@Test
public void getsIntegerWhenWithinBoundsOfOneToTen() throws Exception {
    IntegerAsker asker = mock(IntegerAsker.class);
    when(asker.ask(anyString())).thenReturn(3);

    assertEquals(getBoundIntegerFromUser(asker), 3);
}

@Test
public void asksForNewIntegerWhenOutsideBoundsOfOneToTen() throws Exception {
    IntegerAsker asker = mock(IntegerAsker.class);
    when(asker.ask("Give a number between 1 and 10")).thenReturn(99);
    when(asker.ask("Wrong number, try again.")).thenReturn(3);

    getBoundIntegerFromUser(asker);

    verify(asker).ask("Wrong number, try again.");
}

然后编写通过测试的函数。该函数更加干净,因为您可以删除询问/获取整数重复,并且封装了实际的系统调用。

public static void main(String[] args) {
    getBoundIntegerFromUser(new IntegerAsker(System.in, System.out));
}

public static int getBoundIntegerFromUser(IntegerAsker asker) {
    int input = asker.ask("Give a number between 1 and 10");
    while (input < 1 || input > 10)
        input = asker.ask("Wrong number, try again.");
    return input;
}

对于您的小示例来说,这似乎有些过分,但是如果您正在构建一个更大的应用程序,那么像这样的开发可以很快获得回报。


推荐