如何在单元测试中最小起订量 NetworkStream?
我正在使用起订量和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());
}
I'm using Moq & NUnit as a unit test framework.
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");
}
}
}
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.
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?
Looking forward to your feedback!
Here is the
previous solution:
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 ...
}
UPDATE:
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.
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.
The current solution:
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());
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您不能使用最小起订量模拟 NetworkStream,因为它不是抽象类或接口。但是,您可以在其之上创建一个抽象,并更改您的方法以接受该抽象的实例。可能是这样的:
现在您创建一个实现该接口的类:
现在您可以更改方法签名以使用
IMyNetworkStream
的实例,并使用 Moq 创建IMyNetworkStream< 的模拟/代码>。
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:Now you create a class that implements the interface:
Now you can change your method signature to use an instance of
IMyNetworkStream
and use Moq to create a mock ofIMyNetworkStream
.正如评论中所建议的 - 您是否可以将类型更改为 Stream,以便您在测试期间可以传递 MemoryStream?
As suggested in the comments - is it possible for you to change the type to Stream, so that you, during testing, can pass a MemoryStream instead?
将 NetworkStream 放在一个简单的接口后面(仅包含您需要的调用)并模拟它。
Put the NetworkStream behind a simple interface (with only the calls you need) and mock that.