如何在unit testing中moq一个NetworkStream?

我正在使用Moq和NUnit作为unit testing框架。

我编写了一个方法,给出了一个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"); } } } 

现在我正在考虑为这个方法重新编写unit testing,因为之前编写的测试依赖于真正的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 ... } 

更新:

我再次重写了我的课程。 使用之前的解决方案进行unit testing很顺利,但现实世界的应用程序示例向我展示了为什么我不可能使用(否则很棒)将Stream对象传递给我的方法的建议。

首先,我的应用程序依赖于常量TCP连接。 如果您使用Stream.Read (可能)并且没有数据要接收它将阻止执行。 如果指定超时,则在未收到任何数据时将引发exception。 对于我需要的(相当简单的)应用程序,这种行为是不可接受的。 我只需要一个简单的,连续的TCP连接。 因此,拥有NetworkStream.DataAvailable属性对我的实现至关重要。

目前的解决方案:

我最终为NetworkStream编写了一个接口和一个包装器。 我还最终将临时接收缓冲区的字节数组传递给方法。 unit testing现在运行得相当好。

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

这是我使用的unit testing:

 public void TestReadDataIntoBuffer() { var networkStreamMock = new Mock(); 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(), It.IsAny(), It.IsAny())).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()); } 

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

 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的模拟。

正如评论中所建议的那样 – 您是否可以将类型更改为Stream,以便您在测试期间可以传递MemoryStream?

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