16

ユニットテストフレームワークとしてMoq&NUnitを使用しています。

NetworkStreamオブジェクトをパラメーターとして指定するメソッドを作成しました。

public static void ReadDataIntoBuffer(NetworkStream networkStream, Queue dataBuffer)
{
  if ((networkStream != null) && (dataBuffer != null))
  {
     while (networkStream.DataAvailable)
     {
        byte[] tempBuffer = new byte[512];

        // read the data from the network stream into the temporary buffer
        Int32 numberOfBytesRead = networkStream.Read(tempBuffer, 0, 512);

        // move all data into the main buffer
        for (Int32 i = 0; i < numberOfBytesRead; i++)
        {
           dataBuffer.Enqueue(tempBuffer[i]);
        }
     }
  } 
  else
  {
     if (networkStream != null)
     {
        throw new ArgumentNullException("networkStream");
     }

     if (dataBuffer != null)
     {
        throw new ArgumentNullException("dataBuffer");
     }
  }
}

以前に作成されたテストは実際のNetworkStreamオブジェクトに依存しており、処理があまり適切ではないため、現在、このメソッドの単体テストを書き直すことを検討しています。

NetworkStreamをモックするにはどうすればよいですか?前述のようにMoqを使用しています。それは可能ですか?そうでない場合、この問題をどのように回避できますか?

フィードバックをお待ちしております。

以前の解決策は次の とおりです。

public static void ReadDataIntoBuffer(Stream dataStream, Queue dataBuffer)
{
  if ((networkStream != null) && (dataBuffer != null))
  {
     byte[] tempBuffer = new byte[512];
     Int32 numberOfBytesRead = 0;

     // read the data from the network stream into the temporary buffer
     while ((numberOfBytesRead = dataStream.Read(tempBuffer, 0, 512) > 0)
     {
        // move all data into the main buffer
        for (Int32 i = 0; i < numberOfBytesRead; i++)
        {
           dataBuffer.Enqueue(tempBuffer[i]);
        }
     }
  } 
  else ...
}

アップデート:

クラスをもう一度書き直しました。Stream前のソリューションを使用した単体テストはうまくいきましたが、実際のアプリケーションの例では、オブジェクトをメソッドに渡すという(そうでなければ素晴らしい)提案を使用できない理由がわかりました。

まず、私のアプリケーションは一定のTCP接続に依存しています。を使用しStream.Read(可能です)、受信するデータがない場合、実行がブロックされます。タイムアウトを指定すると、データが受信されない場合に例外がスローされます。この種の動作は、私が必要とする(かなり単純な)アプリケーションには受け入れられません。飾り気のない、一定のTCP接続が必要です。したがって、NetworkStream.DataAvailableプロパティを持つことは私の実装にとって最も重要です。

現在の解決策:

最終的に、NetworkStreamへのインターフェイスとラッパーを作成しました。また、一時的な受信バッファーのバイト配列をメソッドに渡すことになりました。ユニットテストは今ではかなりうまく機能しています。

public static void ReadDataIntoBuffer(INetworkStream networkStream, Queue dataBuffer, byte[] tempRXBuffer)
{
    if ((networkStream != null) && (dataBuffer != null) && (tempRXBuffer != null))
    {
        // read the data from the network stream into the temporary buffer
        while(networkStream.DataAvailable)
        {
            Int32 numberOfBytesRead = networkStream.Read(tempRXBuffer, 0, tempRXBuffer.Length);

            // move all data into the main buffer
            for (Int32 i = 0; i < numberOfBytesRead; i++)
            {
                dataBuffer.Enqueue(tempRXBuffer[i]);
            }
        }
    }
    else ...
}

そして、これが私が使用する単体テストです:

public void TestReadDataIntoBuffer()
{
    var networkStreamMock = new Mock<INetworkStream>();
    StringBuilder sb = new StringBuilder();

    sb.Append(_testMessageConstant1);
    sb.Append(_testMessageConstant2);
    sb.Append(_testMessageConstant3);
    sb.Append(_testMessageConstant4);
    sb.Append(_testMessageConstant5);


    // ARRANGE
    byte[] tempRXBuffer = Encoding.UTF8.GetBytes(sb.ToString());

    // return true so that the call to Read() is made
    networkStreamMock.Setup(x => x.DataAvailable).Returns(true);

    networkStreamMock.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Callback(() =>
        {
            // after the call to Read() re-setup the property so that we
            // we exit the data reading loop again
            networkStreamMock.Setup(x => x.DataAvailable).Returns(false);

        }).Returns(tempRXBuffer.Length);

    Queue resultQueue = new Queue();

    // ACT
    ReadDataIntoBuffer(networkStreamMock.Object, resultQueue, tempRXBuffer);

    // ASSERT
    Assert.AreEqual(Encoding.UTF8.GetBytes(sb.ToString()), resultQueue.ToArray());
}
4

3 に答える 3

17

NetworkStream抽象クラスまたはインターフェースではないため、withmoqをモックすることはできません。ただし、その上に抽象化を作成し、その抽象化のインスタンスを受け入れるようにメソッドを変更することができます。これは次のようなものである可能性があります。

public interface IMyNetworkStream
{
    int Read([In, Out] byte[] buffer, int offset, int size);
    bool DataAvailable {get;}
}

次に、インターフェイスを実装するクラスを作成します。

public class MyNetworkStream : IMyNetworkStream
{
     private NetworkStream stream;

     public MyNetworkStream(NetworkStream ns)
     {
         if(ns == null) throw new ArgumentNullException("ns");
         this.stream = ns;
     }

     public bool DataAvailable
     {
         get
         {
             return this.stream.DataAvailable;
         }
     }

     public int Read([In, Out] byte[] buffer, int offset, int size)
     {
         return this.stream.Read(buffer, offset, size);
     }

}

これで、のインスタンスを使用するようにメソッドシグネチャを変更し、IMyNetworkStreamMoqを使用してのモックを作成できますIMyNetworkStream

于 2010-02-01T13:54:48.327 に答える
6

コメントで示唆されているように、タイプをStreamに変更して、テスト中に代わりにMemoryStreamを渡すことができるようにすることは可能ですか?

于 2010-02-01T13:51:04.620 に答える
1

NetworkStreamを(必要な呼び出しのみを含む)単純なインターフェースの背後に置き、それをモックします。

于 2010-02-01T13:46:58.607 に答える