为了娱乐(并在课堂上用作教学工具),我正在编写类似于JUnitTests的程序。我想用它对高中生编写的代码进行测试。当学生编写使用Scanner通过System.in获取用户输入的代码时,我想“劫持” Scanner以提供我自己的预定用户输入。除了让我的扫描仪成为AutoTester的内部类的愿望之外,我的所有工作都很好。
下面的示例大大简化了,但很清楚。
比方说,一个学生写了这段代码:
import java.util.*;
public class Practice {
public static void practiceScanners() {
Scanner console = new Scanner(System.in);
System.out.print("Type first name, age, and address >> ");
String name = console.next();
int age = console.nextInt();
String address = console.nextLine();
System.out.println(name + " is " + age + " and lives at" + address + ".");
}
}
然后,假设我有一个名为AutoTester的类,看起来像这个伪代码:
public class AutoTester {
public main() {
* use JavaCompiler from ToolProvider to compile Practice.java
* use reflection to invoke Practice.practiceScanners()
* (hopefully) have student code use my Scanner instead of java.util.Scanner
* collect output of running the student code
* compare against expected output
}
//My version of Scanner is an inner class within AutoTester
public class Scanner {
private String script = "Bob 13 42 Maple Drive";
<* implementation not shown *>
}
}
如果将我的Scanner版本移动到它自己的Scanner.java文件中,则可以正常工作。学生代码在查看java.util之前使用我的(本地)扫描程序。但是,当我将Scanner作为内部类移入AutoTester时,学生代码不知道要在那里寻找它。而且我也不想以任何方式编辑学生代码(例如将“扫描仪”实例化行更改为新的AutoTester.Scanner(System.in))
在编译/运行学生程序而不对他们的代码进行任何编辑时,有没有办法让我的AutoTester.Scanner优先于java.util.Scanner?
不幸的是,这种方式行不通。自定义扫描程序的类名称实际上是AutoTester.Scanner
,您在测试类中定义了它,除了看起来相似的名称之外,它与java.util.Scanner没有任何关系。因此,这根本不影响Practice
。
您必须重组Practice
代码,才能将@Inject
(例如CDI与)用作扫描仪。然后使用mockito(@Mock, @InjectMocks)连接扫描仪。不过,那是很多重型机械。作为替代方案,您可以设计Practice
,以使其将Scanner作为构造函数参数或作为属性。
但是您仍然有问题,扫描程序是无法扩展的最终课程。因此,您必须为此目的引入自己的接口,在一种情况下将java.util.Scanner置于幕后,而在另一种情况下将其替换为Scanner。