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?如前所述,我正在使用起订量。有可能吗?如果不是,我该如何解决这个问题?

期待您的反馈!

这是 以前的解决方案:

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

您无法模拟NetworkStreamwith moq,因为它不是抽象类或接口。但是,您可以在其之上创建一个抽象并更改您的方法以接受该抽象的实例。它可能是这样的:

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

}

现在您可以更改方法签名以使用 的实例IMyNetworkStream并使用 Moq 创建IMyNetworkStream.

于 2010-02-01T13:54:48.327 回答
6

正如评论中所建议的 - 您是否可以将类型更改为 Stream,以便您在测试期间可以通过 MemoryStream 代替?

于 2010-02-01T13:51:04.620 回答
1

将 NetworkStream 放在一个简单的接口后面(只有您需要的调用)并模拟它。

于 2010-02-01T13:46:58.607 回答