2011-07-06 76 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(); 
    } 
}