Always block input for testing?

I am doing some unit tests where essentially I need an input stream to lock forever. Right now i'm using this to create input stream

InputStream in = new ByteArrayInputStream("".getBytes()); 

While it works for some time, while the input stream is read before the output stream (what I'm testing) is finished, causing all kinds of chaos.

Essentially I need this input stream to lock forever while reading. The only solution I can think of is to set up an InputStream with a massive buffer so that the other streams end, but this is really a hacky and fragile solution. I have a mockito, but I'm very new to this, and not sure if I can get away from mocking reading without mocking anything else.

Does anyone know of a better solution?


EDIT:

This is my new attempt. It works most of the time, but in other cases, the input stream dies early, which causes the output thread to die (this behavior is intentional). I can’t understand why this sometimes fails.

This is a general test in TestNG for simplicity.

  protected CountDownLatch inputLatch; @BeforeMethod public void botSetup() throws Exception { //Setup streams for bot PipedOutputStream out = new PipedOutputStream(); //Create an input stream that we'll kill later inputLatch = new CountDownLatch(1); in = new AutoCloseInputStream(new ByteArrayInputStream("".getBytes()) { @Override public synchronized int read() { try { //Block until were killed inputLatch.await(); } catch (InterruptedException ex) { //Wrap in an RuntimeException so whatever was using this fails throw new RuntimeException("Interrupted while waiting for input", ex); } //No more input return -1; } }); Socket socket = mock(Socket.class); when(socket.getInputStream()).thenReturn(in); when(socket.getOutputStream()).thenReturn(out); //Setup ability to read from bots output botOut = new BufferedReader(new InputStreamReader(new PipedInputStream(out))); ... } @AfterMethod public void cleanUp() { inputLatch.countDown(); bot.dispose(); } 

For the test, I use readLine() from botOut to get the appropriate number of lines. The problem is that when the output stream dies, readLine() blocks forever, which TestNG hangs. I tried a timeout with mixed results: most of the time it worked, but others could kill the tests, which took a little longer than usual, to check.

My only option is to simply not use threads for this kind of work. The output stream depends on the output queue, so I could just escape from it. The problem is that I am not actually testing writing to the stream, just what will be sent is what bothers me.

+4
source share
5 answers

There seems to be no reliable way to do this. My code in the question only works sometimes, @Moe doesn’t work at all, @Ed offers what I did initially, and @SJuan is what I already do.

It seems like too many things are happening. The input stream that I pass to the class is wrapped in an InputStreamReader, then a buffered reader. Suggestions for other threads within other threads just complicate the problem.

To fix the problem, I did what I had to do initially: create a factory method for InputThread (a thread that actually does the reading), and then override it in my testing. Simple, efficient and 100% reliable.

I suggest everyone who encounters this problem to first try and override the part of your program that reads. If you cannot, then the code I posted is the only semi-reliable code that works in my situation.

+1
source

I would do an InputStream, which, when read (), does wait () on what is held until you finish with the rest of the test. Subclass from FilterInputStream to get everything else free.

+2
source

Mokito is great - I personally am a big fan!

With Mockito, you can do something like the code below. You basically tweak the layout flow, and you tell it to sleep for a very long time when the read method is called on it. Then you can pass this layout to the code you want to test when the thread freezes.

 import static org.mockito.Mockito.*; //... @Test public void testMockitoSleepOnInputStreamRead() throws Exception{ InputStream is = mock(InputStream.class); when(is.read()).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { try { Thread.sleep(10000000000L); return null; } catch (InterruptedException ie) { throw new RuntimeException(ie); } } }); //then use this input stream for your testing. } 
+2
source

Then you will need a different flavor of InputStream. Read the block when there are no more bytes, but with ByteArrayOutputStream they are always available until the end of the stream is found.

I would extend BAOS by changing read () so that it checks for a specific boolean value (if reading is true, if false, wait for the second and loop). Then change this variable from your device code when it is the right time.

Hope that helps

0
source

I created a helper class that extends ByteArrayInputStream for my unit tests. It passes the given byte[] through, but at the end of the stream, instead of returning -1 it waits until close() called. If ten seconds pass, he will refuse and throw an exception.

If you want it to be closed earlier, you can call latch.countdown() yourself.

 import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class BlockingByteArrayInputStream extends ByteArrayInputStream { private CountDownLatch latch; public BlockingByteArrayInputStream(byte[] buf) { super(buf); latch = new CountDownLatch(1); } @Override public synchronized int read() { int read = super.read(); if (read == -1) { waitForUnblock(); } return read; } @Override public int read(byte[] b) throws IOException { int read = super.read(b); if (read == -1) { waitForUnblock(); } return read; } @Override public synchronized int read(byte[] b, int off, int len) { int read = super.read(b, off, len); if (read == -1) { waitForUnblock(); } return read; } private void waitForUnblock() { try { latch.await(10, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new RuntimeException("safeAwait interrupted"); } } @Override public void close() throws IOException { super.close(); latch.countDown(); } } 
0
source

All Articles