0

For fun (and to use in my classroom as a teaching tool) I'm writing a program similar to JUnitTests. I want to use it to run tests on code written by high school students. When a student writes code that gets user input via System.in using a Scanner, I want to "hijack" the Scanner to supply my own predetermined user inputs. I have everything working great except for my desire for my Scanner to be an inner class of the AutoTester.

The example below is grossly oversimplified, but gets the point across.

Let's say a students writes this code:

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 + ".");
    }
}

Then, let's say I have a class called AutoTester that looks something like this pseudocode:

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 *>    
    }

}

If I move my version of Scanner into its own Scanner.java file, this works just fine. The student code uses my (local) Scanner before looking in java.util. But when I move my Scanner into the AutoTester as an inner class the student code doesn't know to go looking for it there. And I don't want to edit the student code in any way (such as changing the Scanner instantiation line to say new AutoTester.Scanner(System.in))

Is there any way to give my AutoTester.Scanner priority over java.util.Scanner when compiling/running the student's program without making any edits to their code?

  • 1
    You can't. The imports will be wrong, the type will be wrong, and if it's an inner class an instance of the outer class will be required to construct it. All you need here is to redirect the standard input. – user207421 Nov 03 '19 at 23:21

1 Answers1

1

Doesn't work that way, unfortunately. The class name of the custom scanner is actually AutoTester.Scanner, you defined it in the test class and apart from the similar looking name it has nothing to do with java.util.Scanner. So this doesn't affect Practice at all.

You'd have to restructure the Practice code either to use @Injects ( e.g. with CDI) for something that works as a scanner. Then use mockito (@Mock, @InjectMocks) to wire your scanner. That's lots of heavy machinery, though. As an alternative you could design Practice so that it takes a Scanner as constructor parameter or as a property.

But then still you have the problem, that Scanner is a final class that can not be extended. So you'd have to introduce your own interface for the purpose, put a java.util.Scanner under the hood in one case, and your alternative Scanner in the other case.

Curiosa Globunznik
  • 3,129
  • 1
  • 16
  • 24