2010-02-01 11 views
15

Sto usando Moq & NUnit come framework di test unitario.Come installare un NetworkStream in un test di unità?

Ho scritto un metodo che viene dato un oggetto NetworkStream come parametro:

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

Ora sto guardando ri-scrittura di mio test di unità per questo metodo dal momento che le prove scritte in precedenza si basano su reali NetworkStream oggetti e non sono molto piacevoli da gestire

Come posso prendere in giro il NetworkStream? Sto usando Moq come accennato in precedenza. È possibile a tutti? In caso contrario, come posso risolvere questo problema?

In attesa di un vostro feedback!

Ecco la precedente soluzione:

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:

ho ri-scritto la mia classe, ancora una volta. Il test delle unità utilizzando la soluzione precedente è andato bene, ma l'esempio dell'applicazione reale mi ha mostrato perché NON è possibile che io usi il (altrimenti ottimo) suggerimento di passare un oggetto Stream nel mio metodo.

Prima di tutto, la mia applicazione si basa su una connessione TCP costante. Se si utilizza Stream.Read (che è possibile) e non ci sono dati da ricevere, bloccherà l'esecuzione. Se si specifica un timeout, verrà generata un'eccezione se non viene ricevuto alcun dato. Questo tipo di comportamento non è accettabile per l'applicazione (piuttosto semplice) di cui ho bisogno. Ho solo bisogno di una connessione TCP senza fronzoli e costante. Pertanto, la proprietà NetworkStream.DataAvailable è fondamentale per la mia implementazione.

La soluzione attuale:

ho finito per scrivere un'interfaccia e un wrapper per NetworkStream. Ho anche finito per passare l'array di byte per il buffer di ricezione temporaneo nel metodo. L'unità di test funziona ora piuttosto bene.

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

Ed ecco la prova di unità che uso:

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

Puoi cambiare il 'NetworkStream' in un' Stream'? Questo potrebbe rendere le cose più facili. –

+0

Credo di poterlo fare. Puoi approfondire come ciò renderebbe le cose più facili? Moq può deridere un oggetto Stream? –

+0

sì, Moq può simulare uno stream poiché è una classe astratta, tuttavia 'Stream' non contiene la proprietà' DataAvailable', quindi suggerisco di utilizzare l'approccio che ho descritto di seguito. –

risposta

15

Non è possibile prendere in giro il NetworkStream con moq poiché non è una classe astratta o un'interfaccia. Puoi comunque creare un'astrazione e modificare il tuo metodo per accettare un'istanza di tale astrazione.Potrebbe essere qualcosa di simile:

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

Ora si crea una classe che implementa l'interfaccia:

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

} 

Ora è possibile modificare la firma metodo da utilizzare un'istanza di IMyNetworkStream e utilizzare Moq per creare un simulazione di IMyNetworkStream.

+0

Grazie per l'elaborazione! Penso di poter evitare di dover utilizzare la proprietà DataAvailable di NetworkStream testando il risultato della chiamata a Read(). O puoi vedere una ragione per cui non dovrebbe funzionare? –

+0

No, probabilmente funzionerebbe pure. Tuttavia, l'implementazione di 'DataAvailable' è leggermente diversa. Potresti voler esaminare l'implementazione nel riflettore in caso di dubbio. –

+0

Vado a testare NetworkStream.DataAvailable prima di chiamare effettivamente il mio metodo ReadDataIntoBuffer(). In questo modo posso passare solo l'oggetto Stream. Grazie per il tuo feedback! –

1

Mettere il NetworkStream dietro una semplice interfaccia (con solo le chiamate è necessario) e modelli che.

+0

Questa è una proposta valida. Mi stavo chiedendo se potevo essere un po 'più pigro e omettere di creare un'interfaccia che potrei prendere in giro ... –

6

Come suggerito nei commenti - è possibile cambiare il tipo in Stream, in modo che durante il test, invece, si possa passare un MemoryStream?

+0

Beh, questo è un buon suggerimento! Non sarei in grado di utilizzare NetworkStream.DataAvailable più come nel mio codice di esempio però. Immagino di poter sostituire l'implementazione della lettura dei dati reali in qualche modo. –

Problemi correlati