junit测试

7xzttuei  于 2021-07-09  发布在  Java
关注(0)|答案(4)|浏览(364)

我必须在一个类中测试一个方法,该类使用scanner类获取输入。

package com.math.calculator;

import java.util.Scanner;

public class InputOutput {

    public String getInput() {
        Scanner sc = new Scanner(System.in);
        return sc.nextLine();
    }
}

我想用junit来测试它,但不知道怎么做。
我试着使用下面的代码,但它不起作用。

package com.math.calculator;

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class InputOutputTest {

    @Test
    public void shouldTakeUserInput() {
        InputOutput inputOutput= new InputOutput();

        assertEquals("add 5", inputOutput.getInput());
    }
}

我也想用mockito(使用mock。。。什么时候。。。然后返回)但不知道怎么做。

zpgglvta

zpgglvta1#

你可以改变主意 System.in 流使用 System.setIn() 方法。
试试这个,

@Test
public void shouldTakeUserInput() {
    InputOutput inputOutput= new InputOutput();

    String input = "add 5";
    InputStream in = new ByteArrayInputStream(input.getBytes());
    System.setIn(in);

    assertEquals("add 5", inputOutput.getInput());
}

您刚刚修改了 System.in 现场。 System.in 基本上是一个 InputStream 上面写着 console (因此在控制台中输入)。但您只是修改了它,让系统从提供的 inputstream 相反。所以它不再从控制台读取,而是从提供的输入流读取。

h79rfbju

h79rfbju2#

您可以使用 TextFromStandardInputStream 系统规则库的规则。

public void MyTest {
  @Rule
  public final TextFromStandardInputStream systemInMock
    = emptyStandardInputStream();

  @Test
  public void shouldTakeUserInput() {
    systemInMock.provideLines("add 5", "another line");
    InputOutput inputOutput = new InputOutput();
    assertEquals("add 5", inputOutput.getInput());
  }
}
3vpjnl9f

3vpjnl9f3#

首先,我假设您的测试的目标是验证用户输入是从扫描器获得的,并且返回的值是扫描器中输入的值。
mocking不起作用的原因是每次在 getInput() 方法。因此,无论你做什么,你的mockito示例永远不会被调用。因此,使该类可测试的正确方法是标识该类的所有外部依赖项(在本例中为 java.util.Scanner 并通过构造函数将它们注入类中。这样,您就可以在测试期间注入模拟扫描仪示例。这是实现依赖注入的基本步骤,而依赖注入又会导致良好的tdd。举个例子可以帮助您:

package com.math.calculator;

    import java.util.Scanner;

    public class InputOutput {

        private final Scanner scanner;

        public InputOutput()
        {
           //the external exposed default constructor 
           //would use constructor-chaining to pass an instance of Scanner.

           this(new Scanner(System.in));
        }

        //declare a package level constructor that would be visible only to the test class. 
      //It is a good practice to have a class and it's test within the same     package.
        InputOutput(Scanner scanner)
        {
            this.scanner  = scanner;
        }

        public String getInput() {

            return scanner.nextLine();
        }
    }

现在你的测试方法是:

@Test
public void shouldTakeUserInput() {
    //create a mock scanner
    Scanner mockScanner = mock(Scanner.class);
    //set up the scanner
    when(mockScanner.nextLine()).thenReturn("add 5");

    InputOutput inputOutput= new InputOutput(mockScanner);

    //assert output
    assertEquals("add 5", inputOutput.getInput());

   //added bonus - you can verify that your scanner's nextline() method is
   //actually called See Mockito.verify
   verify(mockScanner).nextLine();
}

还要注意的是,由于在上面的类中我是使用构造函数注入的,所以我已经声明了scanner示例final。因为我在这个类中没有更多的可变状态,所以这个类是线程安全的。
基于构造函数的依赖注入的概念非常酷,值得在互联网上阅读。它有助于开发良好的线程安全可测试代码。

b1payxdu

b1payxdu4#

除了switching system.in之外,正如codebender也提到的,考虑重构也是如此 getInput() 变成了一个彻底的 getInput(Scanner) 方法,您可以通过创建自己的 Scanner("your\ntest\ninput\n") . 有很多其他的方法来注入你的扫描仪依赖性,比如让你覆盖一个字段进行测试,但是仅仅让一个方法重载是非常容易的,技术上给你更多的灵活性(比如让你添加一个特性来读取文件的输入)。
总的来说,要记住设计要容易测试,而且测试高风险的部件要比测试低风险的部件重。这意味着重构是一个很好的工具,而测试 getInput(Scanner) 可能比测试更重要 getInput() 尤其是你不仅仅是打电话 nextLine() .
我强烈建议不要创建模拟scanner:模拟一个不属于自己的类型不仅是一种不好的做法,而且scanner代表了一个非常大的相关方法api,调用顺序非常重要。在mockito中复制它意味着要么在mockito中创建一个大的假scanner实现,要么模拟一个只测试所做调用的最小实现(如果实现发生了更改,即使您的更改提供了正确的结果,也会中断)。使用真正的扫描器并保存mockito实践,以用于外部服务调用或模拟您定义的小型但未编写的api的情况。

相关问题