如何在单元测试中起订量网络流? [英] How to moq a NetworkStream in a unit test?

查看:14
本文介绍了如何在单元测试中起订量网络流?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Moq &NUnit 作为单元测试框架.

I'm using Moq & NUnit as a unit test framework.

我写了一个方法,它以 NetworkStream 对象作为参数:

I've written a method that is given a NetworkStream object as a parameter:

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 对象并且不是很好处理.

Now I am looking at re-writing my unit tests for this method since the previously written tests rely on real NetworkStream objects and are not very nice to handle.

如何模拟 NetworkStream?如前所述,我正在使用起订量.有可能吗?如果不是,我该如何解决这个问题?

How can I mock the NetworkStream? I'm using Moq as mentioned beforehand. Is it possible at all? If not how could I workaround this problem?

期待您的反馈!

这里是以前的解决方案:

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 对象传递到我的方法中的(否则很好的)建议.

I've re-written my class once again. Unit testing using the previous solution went fine but the real-world application example showed me why it is NOT possible for me to use the (otherwise great) suggestion of passing a Stream object into my method.

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

First off, my application relies on a constant TCP connection. If you use Stream.Read (which is possible) and there is no data to receive it will block the execution. If you specify a timeout an exception will be thrown if no data is received. This kind of behaviour is not acceptable for the (rather simple) application I need. I just need a no-frills, constant TCP connection. Therefore having the NetworkStream.DataAvailable property is paramount to my implementation.

目前的解决方案:

我最终为 NetworkStream 编写了一个接口和一个包装器.我还最终将临时接收缓冲区的字节数组传递给该方法.现在对它进行单元测试效果很好.

I ended up writing an interface and a wrapper to NetworkStream. I also ended up passing the byte array for the temporary receive buffer into the method. Unit testing it now works rather well.

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

这是我使用的单元测试:

And here's the unit test that I use:

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

推荐答案

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

You cannot mock the NetworkStream with moq since it is not an abstract class or an interface. You can however create an abstraction on top of it and change your method to accept an instance of that abstraction. It could be something like this:

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

现在你创建一个实现接口的类:

Now you create a class that implements the interface:

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

Now you can change your method signature to use an instance of IMyNetworkStream and use Moq to create a mock of IMyNetworkStream.

这篇关于如何在单元测试中起订量网络流?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆