Как использовать NetworkStream в модульном тесте? - PullRequest
16 голосов
/ 01 февраля 2010

Я использую 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 ...
}

UPDATE:

Я переписал свой класс еще раз. Модульное тестирование с использованием предыдущего решения прошло нормально, но пример реального приложения показал мне, почему я НЕ могу использовать (в противном случае отличный) совет о передаче объекта 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());
}

Ответы [ 3 ]

16 голосов
/ 01 февраля 2010

Вы не можете смоделировать 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.

6 голосов
/ 01 февраля 2010

Как указано в комментариях - возможно ли изменить тип на Stream, чтобы вы могли вместо этого передать MemoryStream?

1 голос
/ 01 февраля 2010

Поместите NetworkStream за простым интерфейсом (только с нужными вам вызовами) и сделайте это.

...