views:

299

answers:

3

Hi everyone!

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());
}
A: 

Put the NetworkStream behind a simple interface (with only the calls you need) and mock that.

Maxwell Troy Milton King
That is a valid proposal. I was wondering if I could somehow be a bit more lazy and omit creating an interface that I could mock...
Timo Kosig
+4  A: 

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?

kastermester
Well that's a good suggestion! I wouldn't be able to use NetworkStream.DataAvailable anymore as in my example code though. I guess I could replace that implementation of reading the actual data somehow.
Timo Kosig
+5  A: 

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:

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

Now you create a class that implements the interface:

public class MyNetworkStream
{
     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);
     }

}

Now you can change your method signature to use an instance of IMyNetworkStream and use Moq to create a mock of IMyNetworkStream.

klausbyskov
Thanks for elaborating! I think I can circumvent having to use the DataAvailable property of NetworkStream by testing the result of calling Read(). Or can you see a reason why that shouldn't work?
Timo Kosig
No, that would probably work aswell. However, the implementation of `DataAvailable` is a bit different. You may want to look at the implementation in reflector if in doubt.
klausbyskov
I'm going to test NetworkStream.DataAvailable before I'm actually calling my ReadDataIntoBuffer() method. That way I can pass just the Stream object. Thanks for your feedback!
Timo Kosig