2011-07-22 118 views
6

我正在研究一個使用UdpClient的類,並嘗試在過程中使用NUnit和Moq學習/使用TDD方法。模擬UdpClient進行單元測試

我班的裸機部分至今如下:

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); 
    } 
} 



對於發送方法,我在此刻以下的單元測試:

[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地址,儘管只有本地主機,並且該類中的UdpClient正在向其發送數據。因此,據我瞭解,這不是一個真正的單元測試。

問題是,我無法確切地知道該怎麼做。我應該改變我的班級,並將新的UdpClient作爲依賴項傳入嗎?以某種方式模擬IP地址?

掙扎了一下,所以我需要停下來看看我是否在正確的軌道上繼續前進行。任何建議感激!

(使用NUnit 2.5.7,Moq 4.0和C#WinForms。) 。

UPDATE:

好吧,我有我的重構代碼如下:

創建一個IUdpClient接口:

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

創建一個適配器類來包裝UdpClient系統類:

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 CLAS需要通過構造函數注入的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()); 
} 

歡迎任何進一步的意見。

回答

5

如果您只想測試您的類的功能,我會創建一個接口ICommunucator,然後創建一個UdpCommunicator類,它直接包裝所需的UdpClient屬性和方法,而不進行任何檢查和條件。

在你的類中,注入包裝並使用is而不是tf UdpClient。

這樣一來,在測試中,你可以嘲笑ISender和測試。

當然,你不會對包裝本身的測試,但如果它只是一個普通的呼叫轉移,你不需要這個。

基本上,你在正確的方向已經開始,但你添加了一些自定義邏輯,不能被測試的方式 - 所以你需要分割的定製邏輯和通訊部分。

也就是說,你的類變成這樣:

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.... 

所以,在幾個驗證報表您可以檢查是否打開的傳播者被調用時,如果有合適的數據傳遞等

一般 - 你不需要測試系統或第三方類,只有你自己。

如果您的代碼使用這樣的類,使它們可注射。如果這些類不具有虛擬方法(即不能直接模擬它們),或者不實現一些通用接口(如SqlConnection等 - 它實現了IDbConnection),那麼您將創建一個如上所述的純封裝。

另外的可測性的改進,這樣的做法將使其更容易修改你的代碼在將來 - 也就是當你需要溝通的一些其他方法,等您的回覆

+0

感謝。你的意思是說「你可以模擬ICommunicator ...」嗎? – Andy

+0

請參閱編輯 –

+0

是的,現在更有意義,謝謝。 – Andy

相關問題