Reputation: 147
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
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
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
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