2011-07-22 54 views
6

Tôi đang làm việc trên một lớp học sử dụng UdpClient và cố gắng tìm hiểu/sử dụng phương pháp TDD sử dụng NUnit và Moq trong quá trình này.Mock UdpClient để kiểm tra đơn vị

Một trần xương một phần của lớp học của tôi cho đến nay như sau:

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

.
.
Đối Send phương pháp, tôi có kiểm tra đơn vị sau đây vào lúc này:

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

.
.
Tôi không hài lòng với điều đó bởi vì nó đang sử dụng một địa chỉ IP thực, mặc dù chỉ có localhost, và UdpClient bên trong lớp là vật lý gửi dữ liệu đến nó. Vì vậy, đây không phải là một thử nghiệm đơn vị thực sự theo như tôi hiểu nó.

Vấn đề là, tôi không thể xoay quanh chính xác những gì cần làm. Tôi có nên thay đổi lớp học của mình và vượt qua UdpClient mới như một sự phụ thuộc không? Giả lập IPAddress bằng cách nào đó?

Đấu tranh một chút, vì vậy tôi cần dừng lại ở đây để xem tôi có đi đúng hướng hay không trước khi tiếp tục. Bất kỳ lời khuyên nào được đánh giá cao!

(Sử dụng NUnit 2.5.7, Moq 4.0 và C# WinForms.) .
.

UPDATE:

OK, tôi đã refactored mã của tôi như sau:

Tạo một giao diện IUdpClient:

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

.

Tạo một lớp adapter để quấn hệ thống lớp 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 
} 

.

refactored Clas UdpCommsChannel tôi để yêu cầu một thể hiện của một IUdpClient tiêm qua các nhà xây dựng:

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

.

kiểm tra đơn vị của tôi bây giờ trông như thế này:

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

.

Bất kỳ nhận xét nào khác đều được chào đón.

Trả lời

5

Nếu bạn chỉ muốn kiểm tra chức năng của lớp, tôi sẽ tạo một giao diện ICommunucator, sau đó tạo lớp UdpCommunicator, trực tiếp bao bọc các thuộc tính và phương thức UdpClient cần thiết mà không cần kiểm tra và điều kiện.

Trong lớp học của bạn, hãy tiêm trình bao bọc và sử dụng thay cho tf UdpClient.

Bằng cách đó, trong các thử nghiệm, bạn có thể giả lập ISender và kiểm tra.

Tất nhiên, bạn sẽ không có kiểm tra cho trình bao bọc, nhưng nếu đó chỉ là một chuyển tiếp cuộc gọi đơn giản, bạn không cần điều này. Về cơ bản, bạn đã bắt đầu đi đúng hướng, nhưng bạn đã thêm một số logic tùy chỉnh, không thể kiểm tra theo cách đó - vì vậy bạn cần phải tách logic tùy chỉnh và phần giao tiếp.

Ie, lớp học của bạn trở nên như thế này:

public MyClass(ICommunicator comm) 
{ 
    public void Method1(someparam) 
    { 
     //do some work with ICommunicator 
    } 
    .... 
} 

Và thử nghiệm của bạn sẽ trông giống như:

var mockComm = Mock.GetMock<ICommunicator>(); 
mockComm.Setup .... 
var myTestObj = new MyClass(mock.Object); 
MyClass.Method1(something); 

mock.Verify.... 

Vì vậy, trong vài báo cáo Xác minh bạn có thể kiểm tra nếu mở trên người giao tiếp được gọi là, nếu dữ liệu đúng được thông qua, v.v.

Nói chung - bạn không cần phải kiểm tra hệ thống hoặc các lớp của bên thứ ba, chỉ của riêng bạn.

Nếu mã của bạn sử dụng các lớp như vậy, hãy làm cho chúng có thể tiêm được. Nếu các lớp này không có phương thức ảo (tức là bạn không thể giả lập chúng trực tiếp), hoặc không triển khai một số giao diện chung (như SqlConnection, v.v. - nó thực hiện IDbConnection), thì bạn tạo một trình bao bọc đơn giản như đã giải thích ở trên.

Bên cạnh những cải tiến testability, một cách tiếp cận như vậy sẽ làm cho nó dễ dàng hơn để sửa đổi mã của bạn trong tương lai - tức là khi bạn cần một số phương pháp khác của truyền thông, vv

+0

Cảm ơn trả lời của bạn. Bạn có ý nói "... bạn có thể giả lập ICommunicator ..."? – Andy

+0

xin hãy xem chỉnh sửa –

+0

Yep, điều này có ý nghĩa hơn bây giờ, cảm ơn. – Andy