I am working on a class that uses UdpClient and trying to learn / use the TDD approach using NUnit and Moq in this process.
So far, part of my bare segment looks like this:
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 submit 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 am not so happy with this because it uses the actual IP address, although it is only localhost, and the UdpClient inside the class physically sends data to it. Therefore, as I understand it, this is not true unit test.
The problem is that I cannot figure out what to do about it. Should I change my class and pass the new UdpClient as a dependency, maybe? Give up IPAddress somehow?
Fight a bit, so I need to stop here to make sure that I am on the right track before continuing. Any advice appreciated!
(Using NUnit 2.5.7, Moq 4.0, and C # WinForms.).
,
UPDATE:
OK, I updated my code as follows:
Created the IUdpClient interface:
public interface IUdpClient { void Connect(IPEndPoint endpoint); int Send(byte[] data, int num); void Close(); }
.
An adapter class has been created for the shell of 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 }
.
Implemented my UdpCommsChannel class to require an IUdpClient instance introduced through the constructor:
public UdpCommsChannel(IUdpClient client, IPEndPoint endpoint) { this._udpClient = client; this._endPoint = endpoint; }
.
Now my unit test 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 are welcome.