Mock UdpClient for unit testing

Viewed 3187

I am working on a class that utilises a UdpClient, and attempting to learn/utilise a TDD approach using NUnit and Moq in the process.

A bare-bones part of my class so far is as follows:

public UdpCommsChannel(IPAddress address, int port)
{
    this._udpClient = new UdpClient();
    this._address = address;
    this._port = port;
    this._endPoint = new IPEndPoint(address, port);
}

public override void Open()
{
    if (this._disposed) throw new ObjectDisposedException(GetType().FullName);

    try
    {
        this._udpClient.Connect(this._endPoint);
    }
    catch (SocketException ex)
    {
        Debug.WriteLine(ex.Message);
    }
}

public override void Send(IPacket packet)
{
    if (this._disposed) throw new ObjectDisposedException(GetType().FullName);

    byte[] data = packet.GetBytes();
    int num = data.Length;

    try
    {
        int sent = this._udpClient.Send(data, num);
        Debug.WriteLine("sent : " + sent);
    }
    catch (SocketException ex)
    {
        Debug.WriteLine(ex.Message);
    }
}

.
.
For the Send method, I have the following unit test at the moment:

[Test]
public void DataIsSent()
{
    const int port = 9600;

    var mock = new Mock<IPacket>(MockBehavior.Strict);
    mock.Setup(p => p.GetBytes()).Returns(new byte[] { }).Verifiable();

    using (UdpCommsChannel udp = new UdpCommsChannel(IPAddress.Loopback, port))
    {
        udp.Open();
        udp.Send(mock.Object);
    }

    mock.Verify(p => p.GetBytes(), Times.Once());
}

.
.
I'm not that happy with that because it is using an actual IP address, albeit only the localhost, and the UdpClient inside the class is physically sending data to it. Therefore, this is not a true unit test as far as I understand it.

Problem is, I can't get my head around exactly what to do about it. Should I change my class and pass in a new UdpClient as a dependency perhaps? Mock the IPAddress somehow?

Struggling a bit, so I need to stop here to see if I am on the right track before carrying on. Any advice appreciated!

(Using NUnit 2.5.7, Moq 4.0 and C# WinForms.) .
.

UPDATE:

OK, I have refactored my code as follows:

Created an IUdpClient interface:

public interface IUdpClient
{
    void Connect(IPEndPoint endpoint);
    int Send(byte[] data, int num);
    void Close();
}

.

Created an adapter class to wrap the UdpClient system class:

public class UdpClientAdapter : IUdpClient
{
    private UdpClient _client;

    public UdpClientAdapter()
    {
        this._client = new UdpClient();
    }

    #region IUdpClient Members

    public void Connect(IPEndPoint endpoint)
    {
        this._client.Connect(endpoint);
    }

    public int Send(byte[] data, int num)
    {
        return this._client.Send(data, num);
    }

    public void Close()
    {
        this._client.Close();
    }

    #endregion
}

.

Refactored my UdpCommsChannel clas to require an instance of an IUdpClient injected via the constructor:

public UdpCommsChannel(IUdpClient client, IPEndPoint endpoint)
{
    this._udpClient = client;
    this._endPoint = endpoint;
}

.

My unit test now looks like this:

[Test]
public void DataIsSent()
{
    var mockClient = new Mock<IUdpClient>();
    mockClient.Setup(c => c.Send(It.IsAny<byte[]>(), It.IsAny<int>())).Returns(It.IsAny<int>());

    var mockPacket = new Mock<IPacket>(MockBehavior.Strict);
    mockPacket.Setup(p => p.GetBytes()).Returns(new byte[] { }).Verifiable();

    using (UdpCommsChannel udp = new UdpCommsChannel(mockClient.Object, It.IsAny<IPEndPoint>()))
    {
        udp.Open();
        udp.Send(mockPacket.Object);
    }

    mockPacket.Verify(p => p.GetBytes(), Times.Once());
}

.

Any further comments welcome.

1 Answers
Related