Java 使用扫描器对用户输入进行junit测试
我必须在一个类中测试一个方法,该类使用Scanner类接受输入Java 使用扫描器对用户输入进行junit测试,java,junit,mocking,mockito,Java,Junit,Mocking,Mockito,我必须在一个类中测试一个方法,该类使用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 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…when…然后return)来尝试它,但不确定如何操作。您可以使用
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
基本上是一个InputStream
,它从控制台
读取(因此您在控制台中的输入)。但您只是修改了它,让系统从提供的inputstream
读取。因此,它不再从控制台读取,而是从提供的输入流中读取。 < P>除了,还提到了重构,考虑重构,因此<代码> GETINPUT()/代码>变成了一个单行调用,调用一个完整的<代码> GETINPINT(扫描器)< /C> >方法,您可以通过创建自己的<代码> Scanner(“您的\Nest\\NoPIN\\N”)轻松地进行测试。
。还有许多其他方法可以注入扫描仪依赖项,比如创建一个覆盖的字段以进行测试,但只需创建一个方法重载就非常简单,从技术上来说,它可以为您提供更大的灵活性(例如,允许您添加一个功能来读取文件中的输入)
一般来说,请记住设计时要考虑测试的方便性,并且高风险部件的测试要比低风险部件的测试更重。这意味着重构是一个很好的工具,测试getInput(Scanner)
可能比测试getInput()
重要得多,尤其是当您不仅仅是调用nextLine()
时
我强烈建议不要创建模拟扫描器:模拟您不拥有的类型不仅是一种不好的做法,而且扫描器代表了一个非常大的相关方法API,调用顺序很重要。在Mockito中复制它意味着要么在Mockito中创建一个大的假扫描程序实现,要么模拟一个只测试所做调用的最小实现(如果实现发生更改,即使您的更改提供了正确的结果,也会中断)。使用真正的扫描器并保存Mockito实践,以用于外部服务调用或模拟您定义的小型但未编写的API的情况。首先,我假设测试的目标是验证用户输入是否从扫描器获得,返回的值是否是扫描器中输入的值
模拟不起作用的原因是每次都在getInput()
方法中创建actual扫描仪对象。因此,无论您做什么,都不会调用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。因为我在这个类中没有更多的可变状态,所以这个类是线程安全的
基于构造函数的依赖注入的概念非常酷,值得在互联网上阅读。它有助于开发良好的线程安全可测试代码。您可以使用库的TextFromStandardInputStream
规则为命令行界面编写清晰的测试
成功了:)。您能告诉我这是如何工作的吗?优秀的解决方案可能是迄今为止最好的方法的重复,尽管它需要@Stefan您的解决方案工作得很好,添加此依赖项,您就可以开始了!
`com.github.stefanbirkner`系统规则
1.16.0
S不能嘲笑罐头商,因为该类是最终类。
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());
}
}