簡體   English   中英

模擬 UdpClient 進行單元測試

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM