Mangos
Mangos

Reputation: 147

How can I test against console input automaticly by mocking?

When trying to mock console input, the test doesen't behave as i expected

I created a wrapper class for console input, output, and tried mocking its behaviour

public class ConsoleReaderWriter {

public void printLine(String message) {

    System.out.println(message);
}

public String readLine() {
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    String result = "";
    try {
        result = bufferedReader.readLine();
    } catch (IOException e) {
        System.err.print(e);
    }
    return result;

}

}

//the method to be tested

public String readPlayerName() {
    consoleReaderWriter.printLine("> What is your name?");
    String playerName = consoleReaderWriter.readLine();
    return playerName;
}

//my test attempt

@Test
public void testReadPlayerNameShouldReturnNameString() {
    String testName = "John Doe";

    ConsoleReaderWriter testReaderWriter = mock(ConsoleReaderWriter.class);

    when(testReaderWriter.readLine()).thenReturn("John Doe");

    assertEquals(testName, underTest.readPlayerName());
}

I am using Mockito. When i run the test, it prompts me to enter input from the console. The test passes, if i enter the expected name, however, i would like to make it automatic, so that i dont have to enter any input while the test runs. Thanks in advance.

Upvotes: 2

Views: 551

Answers (3)

GhostCat
GhostCat

Reputation: 140417

Here:

ConsoleReaderWriter testReaderWriter = mock(ConsoleReaderWriter.class);

Thing is: that specific testReaderWriter should be used by your object under test. Right now, you are creating a mocked object, that is no way related to your object you intend to verify. So, you can follow the advice given in the other answers and make sure your mock gets actually passed to the object under test.

But a better approach would be: to decouple your production code from the input it is using. Your problems start here:

new InputStreamReader(System.in)

You are creating a reader based on a fixed input. Don't do that.

Instead, you could for example pass an instance of InputStream to your ConsoleReaderWriter object. And then that stream is used for all input. Then you don't need to mock anything, you just prepare a stream with fixed content, and you give that to your object under test.

Another way is to mess with System.in, as outlined here for example.

Upvotes: 1

Gonen I
Gonen I

Reputation: 6107

You need to inject your mocked object into your underTest instance.

In the UnderTest class itself, make sure you don't use 'new' to create your ConsoleReaderWriter dependency, but accept it from the outside instead, for example using a constructor agrument.

It could be something along the lines of:

@Test
public void testReadPlayerNameShouldReturnNameString() {
    String testName = "John Doe";


    ConsoleReaderWriter testReaderWriter = mock(ConsoleReaderWriter.class);
    when(testReaderWriter.readLine()).thenReturn("John Doe");

    UnderTest underTest = new UnderTest(testReaderWriter);            
    assertEquals(testName, underTest.readPlayerName());
}

Upvotes: 1

Peteef
Peteef

Reputation: 405

Please take a look at the example:

@RunWith(MockitoJUnitRunner.class)
public class TestClass {
   @Mock
   ConsoleReaderWriter crw;

   @InjectMocks
   UnderTestClass underTest;

   //Some other fields

   @Test
   public void testReadPlayerNameShouldReturnNameString() {
      String testName = "John Doe";

      when(crw.readLine()).thenReturn("John Doe");

      assertEquals(testName, underTest.readPlayerName());
   }
}

Upvotes: 3

Related Questions