2011-07-06 15 views
4

私は本質的に私が永久にブロックする入力ストリームが必要なところでいくつかのユニットテストをしています。今、私はすべての原因、それは時間の一部を動作しますが、出力ストリームは(私がテストしてる何)が終了する前に、他の回の入力ストリームが読み込まれる入力ストリームを構築するテストのために常に入力ストリームをブロックしていますか?

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

これを使用しています一種の混乱。

本来、この入力ストリームは、読み込み時に永久にブロックする必要があります。私が考えることができる唯一の解決策は、他のスレッドが終了するように大規模なバッファでInputStreamをセットアップすることですが、それは本当にハックと脆い解決策です。私はmockitoを持っているが、私はそれを非常に新しいと私は離れて何かを嘲笑せずに読んで嘲笑で離れて得ることができるかどうかわからない。

もっと良い解決方法を知っている人はいますか?


EDIT:

これが私の新しい試みです。ほとんどの場合は動作しますが、入力スレッドが早期に終了すると出力スレッドが停止する(その動作は意図的です)。どうしてこれが失敗するのか分かりません。

これは、わかりやすくするために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(); 
    } 

テストでは、適切な行数を取得するためにbotOutからreadLine()を使用します。しかし、問題は、出力スレッドが終了すると、readLine()が永久にブロックされ、TestNGがハングアップするということです。私は混在した結果でタイムアウトを試しました。ほとんどの場合は動作しますが、他のテストでは通常よりも少し時間がかかりました。

私の唯一の選択肢は、この種の作業にストリームを使用しないことです。出力スレッドは出力キューに依存しているので、そのスレッドを実行できます。しかし問題は、ストリームに書き込むことを実際にテストしているわけではなく、送られる予定のものだけで、私は気になりません。

答えて

1

これを行うには信頼できる方法はありません。私のコードは時々のみ動作します。@ Moe'sはまったく動作しません。@ Edの提案は私が以前からやっていたことであり、@ SJuanは私がすでにやっていることのようなものです。

あまりにも多くのことが起こっているようです。クラスに与える入力ストリームは、InputStreamReaderでラップされ、Buffered Readerでラップされます。他のストリームの中の他のストリームの提案は、問題をさらに複雑にします。

問題を修正するには、私が以前から行っていたはずのことをしました。InputThread(実際に読み取りを行うスレッド)のファクトリメソッドを作成し、テストでオーバーライドします。シンプルで効果的で信頼性の高い100%

私はこの問題に遭遇する人には、最初に読んでいるプログラムの部分を上書きしようと勧めます。あなたができないなら、私が掲載したコードは、私の状況で働く唯一の半安定的なコードです。

0

次に、別のInputStreamフレーバが必要です。使用可能なバイトがなくなったときにブロックを読み込みますが、ByteArrayOutputStreamを使用すると、ストリームの終わりが見つかるまで常に使用できます。

read()を変更してBAOSを拡張し、あるブール値をチェックするようにします(trueの場合はfalse、falseの場合はfalseを返します)。その後、適切な時期にユニットコードからその変数を変更します。私は(読み取り、InputStreamを作ると思います

2

を助け

希望)、(待ちをして)あなたは、テストの残りの部分で行われているまで、ロックされて開催された何かに。 FilterInputStreamのサブクラスを使用して他のすべてを無料で取得します。

+0

私は、BufferedInputStreamにByteArrayInputStreamをラップしてから、CountDownLatchを待つために 'read()'をオーバーロードしました。 @AfterMethodでのみクリーンアップしても、入力スレッドはまだランダムに死んでいるようです。すべてのIOクラスは 'read()'に依存してデータを取得しますか?私は失敗したテストの数が減っているが、問題は依然として続くと言います。 – TheLQ

+0

read()オーバーロードメソッドをすべてオーバーライドしましたか? – SJuan76

+0

@SJuanちょうど 'read()'、他の読み込みオーバーロードは 'read()'に依存していると思いました。 – TheLQ

2

私は個人的には巨大なファンです。

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()など)をwhen(is.read())に置き換えてください。 –

+0

私はまだ全てが 'read()'に依存していると仮定しています。私は本当にSocketFactoryとSocketを嘲笑しています。 Socketからフェッチされた入力ストリームは、使用される前に複数の読者でクライアントコードによってラップされます。 – TheLQ

0

私はユニットテストのためにByteArrayInputStreamを拡張するヘルパークラスを作成しました。与えられたパイプbyte[]をパイプしますが、ストリームの最後では-1を返す代わりに、close()が呼び出されるまで待機します。 10秒が経過すると、それはあきらめて例外をスローします。

あなたが早く終了したい場合は、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(); 
    } 
} 
関連する問題