Я использую 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 ...
}
ОБНОВЛЕНИЕ:
Я переписал свой класс еще раз. Поблочное тестирование с помощью предыдущего решения пошло прекрасное, но реальный пример приложения показал мне, почему для меня не возможно использовать (в других отношениях большое) предложение передачи a 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());
}
Вы не можете издеваться от NetworkStream
с 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
.
Поместите сеть позади простой интерфейс (только с нужными вами звонками) и издевательства.
Как предложено в комментариях - возможно ли вам изменить тип для потока, чтобы вы, во время тестирования, вместо этого можно пройти память?