1

Issue: I'm trying to test a simple program in Java that requires user input but while I'm able to feed the input to the test, I'm only able to do so in the first called method. The error thrown is java.util.NoSuchElementException: No line found

The setup: The first method ask for user to type a command, after that and depending on the command the method will call on other methods that also ask for user input. This is where the issue arises.

Method to be tested:

public abstract class InputHandler {
// Scanner for commands, login message and instructions
public static void start() {
    String fullCommand;
    String actionCommand;
    String commandId;

    Scanner scanner = new Scanner(System.in);

    // Command loop: asks for valid input and assigns id to variable if necessary
    while(true) {
        System.out.println("\nEnter command:");
        actionCommand = scanner.nextLine().toLowerCase();
         //logic to handle different length commands
        }

        switch (actionCommand) {
            case "new lead":
                newLead();
                break;
            case "show leads":
                showLeads();
                break;
            case "quit":
                try {
                    System.out.println("Goodbye!");
                    Thread.sleep(1000);
                    System.exit(0);
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            default:
                System.out.println("Please enter a valid command");
        }
    }
}

JUnit Test:

@Test
void startTest() {
    System.setIn(new ByteArrayInputStream("new lead\nGustavo Perez\n555555555\nhola@gus.io\nKurts".getBytes()));
    InputHandler.start();
    assertTrue(!DatabaseManager.getLeads().isEmpty());
}

Again the issue is on the test when the method is called, while it passes "new lead" as standard input (the first string in the byte array) to the Scanner, after the method start() calls the newLead() it no longer continues to pass the values in the byte array.

Thanks for any help!

GustavoM01
  • 11
  • 3
  • 1
    I strongly suggest you refactor your code to simply accept a `Scanner` instance from outside, then testing it will become much easier. Additionally I suggest you split your read-loop from the command-parsing code, then you can test individual commands simply by calling their respective methods instead of constructing a string that should lead to those method being called. Also: your loop will never exit unless the `quit` command is provided in which case the whole VM will close, which also makes testing extremely hard/annoying. – Joachim Sauer Jul 30 '21 at 16:36
  • What would be the benefit of accepting a `Scanner` from outside? Wouldn't be the same just using an outside instance? You mean splitting the read-loop how? The methods called (like newLead()) are all outside this method, they are just called depending on the command. This is a console program with no GUI, how does user input commands if there is no `Scanner` waiting for input? That's why I wrote a command to exit the program and why the loop never exits until is commanded by the user. – GustavoM01 Jul 30 '21 at 17:30
  • The advantage is that you wouldn't have to depend on setting global state (`System.in`) to some other value, which is quite fragile (it's easy to forget to undo and incredibly easy for one test to influence another this way) and you can reduce all methods to input -> output (as opposed to (input + some global state) -> output. And in the *real* `main` method you simply construct a `Scanner` that takes `System.in` (as you do right now) and pass that into the `menu()` method. Similarly, in the "real" `main` method you can simply `System.exit()` when the `menu()` method returns. – Joachim Sauer Jul 30 '21 at 18:29

0 Answers0