我想在 junit/mockito 下的测试用例中模拟控制台输入,任何人都可以帮助我。我的源代码:
控制台源.java
import java.io.Console;
public class ConsoleSrc {
public static String readFromConsole() {
String str = null;
try {
Console con = System.console();
System.out.println("The console object is: " + con);
str = con.readLine();
System.out.println("String is : " + str);
} catch (Exception ex) {
ex.printStackTrace();
}
return str;
}
public static void main(String[] args) {
ConsoleSrc cs = new ConsoleSrc();
String str = cs.readFromConsole();
System.out.println("String is : " + str);
}
}
测试代码控制台测试.java
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ConsoleTest {
@Test
public void ConsoleSrcTestSuccess() {
ConsoleSrc cs = mock(ConsoleSrc.class);
when(cs.readFromConsole()).thenReturn("##This is not console##");
assertEquals(cs, "This is not console");
}
}
另一个答案是正确的,但最终,你在这里看到的是一个"设计"问题;修复它将使你的代码更容易测试。
您的问题源于您要测试系统控制台的事实。但那是...其实是个坏主意。
你看,最后,你会想要确保你的程序的一部分有一个"源",它可以从中"读取"信息。您"固定"该源作为控制台。这似乎是合乎逻辑的,但实际上是错误的。
您希望从特定的实现中抽象出来;相反,您希望尽可能使用接口或"基类"。例如:java.io.Reader。如果这样做,则可以将代码编写为:
public class ConsoleReader {
private final Reader source;
public ConsoleReader() { this ( System.console.reader() ); }
ConsoleReader(Reader source) { this.source = source; }
以上允许您:
- 使用默认的 no-arg 构造函数创建一个 ConsoleReader 对象;然后它将从 System.console 读取
- 但是,您也可以使用其他构造函数,并为所测试的类提供任何类型的读取器。例如:由Mockito创建的模拟读者。
现在,您不必在控制台中模拟方法;但您可以模拟Reader;并且可以自由地模拟该类的任何方法!
长话短说:你想出了一个不灵活的设计,这也很难测试。答案不是使用模拟框架技巧来规避这个问题;但要改进设计;所以可以更容易地测试!
是的,Console
类是final
的,所以 Mockito 不能通过创建子类直接嘲笑它。若要解决此问题,必须在另一个方法或包装类中隔离交互。然后你模拟此方法或包装器。
代码中的具体问题:你不能模拟静态方法。只需从readFromConsole()
中删除static
修饰符即可。
对代码示例的一些观察:
- 根据 Javadoc 的
System.console()
,它返回"系统控制台,如果有的话,否则为 null.",因此您的生产代码应该处理null
情况。 - 您可以使用
System.in
而不是使用Console
,例如System.setIn(new ByteArrayInputStream("This is not console".getBytes(StandardCharsets.UTF_8)));
- assertEquals 调用没有意义,原因有两个:
- 将
cs
作为ConsoleSrc
实例与String
进行比较将始终失败 - 对模拟返回值的断言不会测试您的代码,而是模拟框架
- 将