2011-07-06 34 views
4

我正在做一些单元测试,基本上我需要输入流永远阻止。现在,我用这个来构建输入流总是阻止输入流进行测试?

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

虽然它工作在某些时候,其他时间输入流输出流前阅读(什么我测试)完成后,会导致所有各种浩劫。

基本上我需要这个输入流在读取时永远阻塞。我能想到的唯一解决方案是使用大量缓冲区设置InputStream,以便其他线程完成,但这是一个非常棘手和脆弱的解决方案。我确实有mockito,但我对它很陌生,并不确定我是否可以在不嘲笑其他任何东西的情况下嘲笑阅读。

有谁知道更好的解决方案?


编辑:

这是我的新的尝试。它大部分时间都在工作,但其他时候输入线程会早死,导致输出线程死掉(这种行为是故意的)。我似乎无法弄清楚为什么这有时会失败。

这是TestNG为简明起见而进行的一般测试。

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(); 
    } 

对于测试我使用readLine()从botOut得到线的适当数量。但问题是,当输出线程死亡时,readLine()永远阻止挂起TestNG。我已经尝试了一个混合结果的超时:大部分时间它会工作,但其他人会杀死比正常测试花费的时间稍长的测试。

我唯一的选择就是不使用流进行这种工作。输出线程依赖于输出队列,所以我可以运行它。但问题是我没有真正测试写入流,只是将发送什么,这会打扰我。

回答

1

似乎没有任何可靠的方法来做到这一点。我在这个问题中的代码有时只是起作用,@ Moe's根本不起作用,@ Ed的建议是我最初的做法,@ SJuan's就是我已经在做的事情。

似乎有太多的东西正在进行。我给这个类的输入流包装在一个InputStreamReader中,然后是一个Buffered阅读器。对其他数据流中的其他数据流的建议使问题更加复杂化。

为了解决这个问题,我做了我应该做的东西:为InputThread(实际上是读取的线程)创建一个工厂方法,然后在我的测试中覆盖。简单,有效,100%可靠。

我建议任何人遇到这个问题,首先尝试并重写你的程序的部分读取。如果你不能,那么我发布的代码是唯一符合我的情况的半可靠代码。

0

然后你需要另一个InputStream的味道。当没有更多字节可用时读取块,但使用ByteArrayOutputStream时,它们始终可用,直到找到流结束为止。

我会通过改变read()来扩展BAOS,所以它会检查某个布尔值(如果为true,则读取,如果false等待一秒钟并循环)。然后在合适的时间从单元代码中更改该变量。

希望帮助

2

我会做的InputStream的是,阅读()时,做的东西,得举办锁定,直到你与测试的其余部分做一个wait()。 FilterInputStream的子类可以免费获得其他所有内容。

+0

奇怪的是,我将ByteArrayInputStream包装在BufferedInputStream中,然后覆盖'read()'以等待CountDownLatch。即使只在@AfterMethod中清理,输入线程似乎仍然是随机死亡。所有IO类都依赖'read()'获取数据吗?我会说虽然失败的测试数量已经下降,但问题仍然存在。 – TheLQ

+0

你重写了所有的read()重载方法吗? – SJuan76

+0

@SJuan只读'()',我认为另一个读过载取决于'read()' – TheLQ

2

Mockito是伟大的 - 我个人是一个巨大的球迷!

随着Mockito,你可以做类似下面的代码。你基本上建立了一个流模拟,并且当它调用“read”方法时,你告诉它睡了很长时间。然后,您可以将此模拟传递到您想要在流挂起时测试的代码中。

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. 
} 
+0

我用CountDownLatch和@AfterMethod做了类似的事情,但InputStream似乎仍然是随机死亡,尽管失败的测试数量已经减少。 – TheLQ

+0

确保在你想要阻塞的代码中使用的read方法的实际变体(例如read(some byte [],anyInt(),anyInt()))来替换(is.read() –

+0

我仍然假定一切都依赖于'read()'。我真的在嘲笑一个SocketFactory和Socket。在使用之前,从Socket获取的输入流在客户端代码中被多个阅读器封装。 – TheLQ

0

我创建了一个辅助类,它为我的单元测试扩展了ByteArrayInputStream。它通过管道传递给定的byte[],但在流的末尾而不是返回-1,它将等待直到close()被调用。如果超过十秒,它会放弃并引发异常。

如果您希望提前关闭,您可以自己拨打latch.countdown()

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(); 
    } 
}