[英]Mock UdpClient for unit testing
我正在研究使用 UdpClient 的 class,並嘗試在此過程中使用 NUnit 和 Moq 學習/利用 TDD 方法。
到目前為止,我的 class 的基本部分如下:
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);
}
}
.
.
對於 Send 方法,我目前有以下單元測試:
[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());
}
.
.
我對此並不滿意,因為它使用的是實際的 IP 地址,盡管只有 localhost,並且 class 內的 UdpClient 正在向它物理發送數據。 因此,據我所知,這不是一個真正的單元測試。
問題是,我不知道該怎么做。 我是否應該更改我的 class 並將新的 UdpClient 作為依賴項傳入? 以某種方式模擬 IPAddress?
有點掙扎,所以我需要在這里停下來看看我是否走在正確的軌道上,然后再繼續。 任何建議表示贊賞!
(使用 NUnit 2.5.7、Moq 4.0 和 C# WinForms。)。
.
更新:
好的,我已經重構了我的代碼如下:
創建了一個 IUdpClient 接口:
public interface IUdpClient
{
void Connect(IPEndPoint endpoint);
int Send(byte[] data, int num);
void Close();
}
.
創建了一個適配器 class 來包裝 UdpClient 系統 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
}
.
重構我的 UdpCommsChannel 類以要求通過構造函數注入的 IUdpClient 實例:
public UdpCommsChannel(IUdpClient client, IPEndPoint endpoint)
{
this._udpClient = client;
this._endPoint = endpoint;
}
.
我的單元測試現在看起來像這樣:
[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());
}
.
歡迎任何進一步的評論。
如果你只想測試你的 class 的功能,我會 go 創建一個接口 ICommunucator,然后創建一個 class UdpCommunicator 和所需的屬性,無需任何檢查客戶端包裝和方法。
在您的 class 中,注入包裝器並使用 is 而不是 tf UdpClient。
這樣,您可以在測試中模擬 ISender 並進行測試。
當然,你不會對包裝器本身進行測試,但如果它只是一個普通的呼叫轉移,你就不需要這個。
基本上,您已經朝着正確的方向開始,但是您添加了一些自定義邏輯,無法以這種方式進行測試-因此您需要拆分自定義邏輯和 comm 部分。
即,你的 class 變成這樣:
public MyClass(ICommunicator comm)
{
public void Method1(someparam)
{
//do some work with ICommunicator
}
....
}
您的測試將如下所示:
var mockComm = Mock.GetMock<ICommunicator>();
mockComm.Setup ....
var myTestObj = new MyClass(mock.Object);
MyClass.Method1(something);
mock.Verify....
因此,在幾個驗證語句中,您可以檢查是否調用了通信器上的 Open、是否傳遞了正確的數據等。
一般來說 - 你不需要測試系統或第三方類,只需要你自己的。
如果您的代碼使用這樣的類,請使它們可注入。 如果這些類沒有虛擬方法(即您不能直接模擬它們),或者沒有實現一些通用接口(如 SqlConnection 等 - 它實現了 IDbConnection),那么您將創建一個如上所述的普通包裝器。
除了可測試性改進之外,這種方法將使將來修改代碼變得更加容易 - 即當您需要其他一些通信方法等時。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.