95

Please provide pointers to help me mock that java InputStream object. This is the line of code that I would wish to Mock:

InputStreamReader inputData = new InputStreamReader(System.in);
bufferdReader = new BufferedReader(inputData);
bufferdReader.readLine(); 
Reji
  • 3,426
  • 2
  • 21
  • 25
  • 1
    Is your problem the System.in ? You probably want to refactor your code so that another InputStream is passed instead of System.in ; then you can use any Mocking framework (like mentionned in the answers) to mock this InputStream. – phtrivier Jun 16 '11 at 12:09

11 Answers11

131

You could use commons-io to create some stub input streams:

InputStream stubInputStream = 
     IOUtils.toInputStream("some test data for my input stream", "UTF-8");
Eric
  • 2,268
  • 2
  • 16
  • 24
  • 1
    If you want to use the stream more than once, how would you reset it after use? – exic Nov 21 '13 at 09:39
  • You could wrap it in a BufferedInputStream and use mark() and reset(). But probably easier to just instantiate a new one in your setup method (assuming we're still talking about testing here). – Eric Dec 09 '13 at 15:43
  • 1
    since v2.3 you need to add the Charset, for ex: `toInputStream("someString", "UTF-8")`. The version with only String as parameters is deprecated. – JonyD Feb 13 '17 at 11:20
  • Thanks @JonyD! I updated the answer to include the charset parameter. – Eric Feb 21 '17 at 19:44
  • 1
    My favorite answer! I would just add that instead of using the non type safe string, you could be using `StandardCharsets.UTF_8` – dcasadevall Aug 02 '19 at 20:18
128

You could just use a ByteArrayInputStream and fill it with your test data.

@Brad's example from the comments:

InputStream anyInputStream = new ByteArrayInputStream("test data".getBytes());
Josh Johnson
  • 10,729
  • 12
  • 60
  • 83
pap
  • 27,064
  • 6
  • 41
  • 46
65
BufferedReader bufferedReader = org.mockito.Mockito.mock(BufferedReader.class);
when(bufferedReader.readLine())
  .thenReturn("first line")
  .thenReturn("second line");

org.junit.Assert.when(new Client(bufferedReader).parseLine())
  .thenEquals(IsEqual.equalTo("first line"));
Boris Pavlović
  • 63,078
  • 28
  • 122
  • 148
21

I disagree with the selected answer for this question. Mocking frameworks like Mockito are nice and all, however when standard java api is available you might consider using that instead.

i.e.

BufferedReader reader = new BufferedReader(new StringReader("some string"));

Why use a Mock object in your test classes when you could use a real one with all its state and behaviour?

To see more about how this works, you could look up the 'decorator' design pattern.

John Deverall
  • 5,954
  • 3
  • 27
  • 37
  • 5
    Here's a usecase: mock multiple different invocations, e.g. in a loop (wrong input1 -> wrong input2 -> correct input3). – Jan Groth Mar 21 '14 at 12:08
  • Keep your InputStream code and your Reader code in separate classes, just like the java api does. Have your test class pass an implementation of Reader, not InputStream, and in production code, also pass an implementation of Reader to the class that is reading your data / doing your data processing. – John Deverall Mar 27 '16 at 09:10
4
@Test
    public void testReadFile() {
    TestClass ClassName = Mockito.mock(TestClass.class);
     InputStream in = Mockito.mock(InputStream.class);
     InputStreamReader inr =Mockito.mock(InputStreamReader.class);
     BufferedReader bufferedReader =Mockito.mock(BufferedReader.class);
       try {
         PowerMockito.whenNew(InputStreamReader.class).withArguments(in).thenReturn(inr);
         PowerMockito.whenNew(BufferedReader.class).withArguments(inr).thenReturn(bufferedReader);
         String line1 = "example line";
         PowerMockito.when(bufferedReader.readLine()).thenReturn(line1).thenReturn(null);
         method return type = Whitebox.invokeMethod(ClassName, "MethodName", arguement);
         assertEquals("result is::","expected", actual);
     } catch (Exception e) {
         e.printStackTrace();
     }
 }
Pentayya
  • 41
  • 2
3
String testString = "test\nstring";
InputStream stream = new ByteArrayInputStream(testString.getBytes(StandardCharsets.UTF_8));

BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

Assert.assertEquals("test", reader.readLine());
Assert.assertEquals("string", reader.readLine());
eyveer
  • 88
  • 5
3

Change your object so it is easier to test, something like this:

public MyObject {
    private InputStream inputStream;

    public void setInputStream(InputStream inputStream) {this.inputStream = inputStream;}

    public void whatever() {
        InputStreamReader inputData = new InputStreamReader(inputStream);
        bufferdReader = new BufferedReader(inputData);
        bufferdReader.readLine(); 
    }
}

then when you use your object initialize its inputStream first:

MyObject myObject = new MyObject();
myObject.setInputStream(System.in);

Now you have an object where you can test it using any implementation of InputStream you want (ByteArrayInputStream is a good one to try).

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
1

The best solution i found is use

final InputStream inputStream1 = IOUtils.toInputStream("yourdata");

and then wrap the inpustream in bufferedReader best way to write test around input Stream

Pritesh
  • 31
  • 3
  • 1
    This adds another dependency on IOUtils - which makes your test code larger. Not the desired effect we want from small unit tests... – IgorGanapolsky Mar 22 '16 at 14:54
0

Assuming you are using Maven you can put a resource into "src/test/resources/" folder let's say "src/test/resources/wonderful-mock-data.xml". Then in you jUnit your can do:

    String resourceInputFile = "/database-insert-test.xml";
    URL url = this.getClass().getResource(resourceInputFile);
    Assert.assertNotNull("Can't find resource " + resourceInputFile, url);

    InputStream inputStream = url.openStream();

    // Now you can just use the inputStream for method calls requiring this param
    (...)

In this example the url varialble will be null if the given resource can't be found inside current classpath. This approach allows you to put multiple scenarios inside different resourceInputFile(s)... Also remember that all kind of resources under "src/test/resources/" (not just xml files, any kind like txt, html, jpeg, etc.) are normaly available as classpath resources from all jUnit tests.

A. Masson
  • 2,287
  • 3
  • 30
  • 36
0
when(imageService.saveOrUpdate(Matchers.<Image>anyObject())).thenReturn(image);

Reference http://www.javased.com/?api=org.mockito.Matchers

取一个好的名字
  • 1,677
  • 14
  • 16
0

You can also mock InpuStream use this guide: https://www.baeldung.com/java-mocking-inputstream

For example:

    @Test
    public void givenSimpleImplementation_shouldProcessInputStream() throws IOException {
        int byteCount = processInputStream(new InputStream() {
            private final byte[] msg = "Hello World".getBytes();
            private int index = 0;
            @Override
            public int read() {
                if (index >= msg.length) {
                    return -1;
                }
                return msg[index++];
            }
        });
        assertThat(byteCount).isEqualTo(11);
    }

    int processInputStream(InputStream inputStream) throws IOException {
        int count = 0;
        while(inputStream.read() != -1) {
            count++;
        }
        return count;
    }
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/34446496) – Miss Skooter May 25 '23 at 20:45