mrs
mrs

Reputation: 217

How to mock console user input in mockito test cases

I want to mock the console input in test cases under junit/mockito can any one please help me. My source code:

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

Test code ConsoleTest.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");
  }
}

Upvotes: 1

Views: 4330

Answers (2)

GhostCat
GhostCat

Reputation: 140407

The other answer is correct, but in the end, you are rather looking at a "design" problem here; and fixing that will make your code much easier to test.

Your problem originates from the fact that you want to test the System console. But that is ... actually a bad idea.

You see, in the end, you will want to make sure that one part of your program has a "source" where it can "read" information from. You "fixated" that source to be the console. That seems logical but is actually wrong.

You want to abstract from specific implementations; instead you want to use interfaces or "base classes" where possible. For example: java.io.Reader. If you do that, you could write your code as:

public class ConsoleReader {
  private final Reader source;

  public ConsoleReader() { this ( System.console.reader() ); }
  ConsoleReader(Reader source) { this.source = source; }

The above allows you to:

  • Create a ConsoleReader object using the default no-arg constructor; and then it will read from System.console
  • But, you can also use the other constructor, and provide any kind of reader to your class under test. For example: a mocked Reader, created by Mockito.

And now you don't have to mock a method in Console; but you mock Reader; and are free to mock any method of that class!

Long story short: you came up with an inflexible design, that is also hard to test. The answer is not to circumvent that problem using mocking framework tricks; but to improve the design; so it can be tested easier!

Upvotes: 2

Jens Piegsa
Jens Piegsa

Reputation: 7485

Yes, the Console class is final, so Mockito can not directly mock it by creating a sub-class. To work around this, you have to isolate the interaction in another method or a wrapper class. Then you mock this method or the wrapper.

The concrete problem in your code: You can not mock a static method. Just remove the static modifier from readFromConsole().

Some observations on your code example:

  • according to the Javadoc of System.console() it returns "The system console, if any, otherwise null.", so your production code should handle the null case.
  • instead of using Console you could read from System.in which can be switched by e.g. System.setIn(new ByteArrayInputStream("This is not console".getBytes(StandardCharsets.UTF_8)));
  • the assertEquals call does not make sense for two reasons:
    • comparison of cs as an instance of ConsoleSrc with a String will always fail
    • assertions on a mocked return value would not test you code but the mocking framework

Upvotes: 1

Related Questions