2010-02-01 3 views
15

Я использую Moq & NUnit как единый тестовый фреймворк.Как moq NetworkStream в модульном тесте?

Я написал метод, который дается объект 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()); 
} 
+2

Вы можете изменить 'NetworkStream' в' Stream'? Это может облегчить ситуацию. –

+0

Думаю, я мог бы это сделать. Можете ли вы объяснить, как это облегчит ситуацию? Может ли Moq издеваться над объектом Stream? –

+0

Да, Moq может издеваться над потоком, поскольку он является абстрактным классом, однако 'Stream' не содержит свойство DataAvailable', поэтому я предлагаю вам использовать подход, который я изложил ниже. –

ответ

15

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

+0

Спасибо за разработку! Я думаю, что могу обойтись, чтобы использовать свойство DataAvailable NetworkStream, проверив результат вызова Read(). Или вы видите причину, почему это не должно работать? –

+0

Нет, это, вероятно, будет работать. Однако реализация 'DataAvailable' немного отличается. Если вы сомневаетесь, вы можете посмотреть на реализацию в отражателе. –

+0

Я собираюсь протестировать NetworkStream.DataAvailable, прежде чем я назову свой метод ReadDataIntoBuffer(). Таким образом, я могу передать только объект Stream. Спасибо за ваш отзыв! –

1

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

+0

Это действующее предложение. Мне было интересно, могу ли я как-то немного лениться и опустить создание интерфейса, который я мог бы издеваться ... –

6

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

+0

Хорошо, это хорошее предложение! Я бы больше не смог использовать NetworkStream.DataAvailable, как в моем примере кода. Наверное, я мог бы заменить эту реализацию как-то считывать фактические данные. –

Смежные вопросы