Mocking objects that change arguments

Most mocking framework know how to mock an interface or (abstract) class and fake the method calls that are performed on these mocks. However, hardly any of them can do anything other than provide a return value when the mock method is called.

This poses a serious problem when ref or out parameters are provided or when reference types are used that need to be manipulated as part of the mock method. Here’s a scenario in which I needed to do exactly that.

The System.Net.Sockets.Socket class can send and receive raw data through a byte[] buffer. Take a look at some of the signatures for the Socket::Send method:

public int Receive(byte[] buffer)

public int Receive(IList<ArraySegment<byte>> buffers, SocketFlags socketFlags);

When the Receive method is called, a byte[] buffer or list of ArraySegments of byte buffers is passed. The return value of Receive will indicate the actual number of bytes that were received. But, … more importantly, the buffer now contains the bytes read from the socket. How would you go about mocking this?

At a first impression you could think that you would create a wrapper or abstraction of the Socket, give it an interface and mock it from there. True, but this still leaves you with a new class to test. If you strive for a high code coverage, or want to do an integration test up until the Socket level, then this offers no way out.

Enter stage for TypeMock. Using this mocking framework you can handle method calls on mocked objects and have dynamic return values. The key to this all is the DynamicReturnValue class. I wrote this extension method for the Socket class. This extension method prepares a mocked Socket object to fake reception of network data. This data is provided in a packet (an ArraySegment of byte). From this packet the data is read and transferred to the buffer that is passed to the Receive method when it is called later on.

public static void FakeReceive(this Socket socket, ArraySegment<byte> packet)

{

  Mock<Socket> mock = MockManager.GetMockOf<Socket>(socket);

  Assert.IsNotNull(mock, “Socket is not mocked. Supply a mocked socket object”);

 

  mock

    .ExpectAndReturn(“Receive”, new DynamicReturnValue(

        delegate(object[] parameters, object context)

        {

          byte[] buffer = (byte[])parameters[0];

 

          int offset;

          int length;

          if (parameters.Length == 1)

          {

            offset = packet.Offset;

            length = packet.Count;

          }

          else

          {

            offset = (int)parameters[1];

            length = (int)parameters[2];

          }

 

          // Check whether packet has enough data

          Assert.IsTrue(packet.Count <= length, “Size of packet is too small for expected data received from socket.”);

          Array.Copy(packet.Array, packet.Offset, buffer, offset, length);

 

          return length;

        }));

}

In this method I expect that the extended Socket object is a mocked object. I arrange the mock to expect and return a DynamicReturnValue for the Receive method. The constructor for the DynamicReturnValue takes a delegate that is called instead of the Receive method on the mocked object. The constructor takes two parameters. The most important one is the object[] parameters, that holds the parameters/arguments of the method called (Receive in our case).

The ExpectAndReturn method does not know which overload of Receive is called. You should have enough control over the situation to know which one it is. The FakeReceive method assumes that the overloads are either of these:

public int Receive(byte[] buffer);

public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags);

For the first overload the number of parameters is 1. The entire packet is transferred to the buffer in that case. Otherwise the requested number of bytes is read into the buffer. The provided packet should contain enough bytes. In both cases the number of bytes read is returned.

The next code fragment shows how to use the FakeReceive method in a test fixture.

MockObject<Socket> mock = MockManager.MockObject<Socket>();

Socket socket = mock.Object;

 

byte[] packet = DeploymentItemHelper.LoadBinaryItem(@”GameSpyMaster2MultiPacketResponseGameSpyMasterResponseHeader.bin”);

socket.FakeReceive(packet, 1);

socket.FakeReceive(new ArraySegment<byte>(packet, 1, 16));

socket.FakeReceive(new ArraySegment<byte>(packet, 17, 9));

 

GameSpyMaster2Response response = new GameSpyMaster2Response(key, validation);

response.Receive(socket);

First, the mock Socket is created. Next, data is loaded from a binary test file. The socket is prepared for three consecutive Receive calls that read more and more from the binary data. Lastly, the GameSpyMaster2Response object is tested by calling the Receive method (not the one from Socket, but of the object under test) passing in the mocked and prepared socket.

Works like a charm. There’s room for improvement, though. You could allow for faking more overloads of Receive. I just called YAGNI and went on.

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s