2010-02-01 38 views
15

Tôi đang sử dụng Moq & NUnit làm khung kiểm tra đơn vị.Làm thế nào để moq một NetworkStream trong một bài kiểm tra đơn vị?

Tôi đã viết một phương pháp mà được cho một đối tượng NetworkStream như một tham số:

public static void ReadDataIntoBuffer(NetworkStream networkStream, Queue dataBuffer) 
{ 
    if ((networkStream != null) && (dataBuffer != null)) 
    { 
    while (networkStream.DataAvailable) 
    { 
     byte[] tempBuffer = new byte[512]; 

     // read the data from the network stream into the temporary buffer 
     Int32 numberOfBytesRead = networkStream.Read(tempBuffer, 0, 512); 

     // move all data into the main buffer 
     for (Int32 i = 0; i < numberOfBytesRead; i++) 
     { 
      dataBuffer.Enqueue(tempBuffer[i]); 
     } 
    } 
    } 
    else 
    { 
    if (networkStream != null) 
    { 
     throw new ArgumentNullException("networkStream"); 
    } 

    if (dataBuffer != null) 
    { 
     throw new ArgumentNullException("dataBuffer"); 
    } 
    } 
} 

Bây giờ tôi đang xem xét lại viết bài kiểm tra đơn vị của tôi cho phương pháp này kể từ khi các cuộc thử nghiệm bằng văn bản trước đây dựa vào NetworkStream thực các đối tượng và không phải là rất tốt đẹp để xử lý.

Tôi làm cách nào để giả lập NetworkStream? Tôi đang sử dụng Moq như đã đề cập trước đó. Có thể ở tất cả? Nếu không làm thế nào tôi có thể giải quyết vấn đề này?

Mong nhận được phản hồi của bạn!

Đây là giải pháp trước:

public static void ReadDataIntoBuffer(Stream dataStream, Queue dataBuffer) 
{ 
    if ((networkStream != null) && (dataBuffer != null)) 
    { 
    byte[] tempBuffer = new byte[512]; 
    Int32 numberOfBytesRead = 0; 

    // read the data from the network stream into the temporary buffer 
    while ((numberOfBytesRead = dataStream.Read(tempBuffer, 0, 512) > 0) 
    { 
     // move all data into the main buffer 
     for (Int32 i = 0; i < numberOfBytesRead; i++) 
     { 
      dataBuffer.Enqueue(tempBuffer[i]); 
     } 
    } 
    } 
    else ... 
} 

UPDATE:

Tôi đã viết lại lớp học của tôi một lần nữa. Thử nghiệm đơn vị sử dụng giải pháp trước đã ổn nhưng ví dụ ứng dụng trong thế giới thực đã cho tôi thấy tại sao tôi KHÔNG thể sử dụng đề xuất (nếu không tuyệt vời) khi chuyển đối tượng Stream vào phương thức của tôi.

Trước hết, ứng dụng của tôi dựa trên kết nối TCP không đổi. Nếu bạn sử dụng Stream.Read (có thể) và không có dữ liệu để nhận nó sẽ chặn việc thực hiện. Nếu bạn chỉ định thời gian chờ, ngoại lệ sẽ bị ném nếu không nhận được dữ liệu nào. Loại hành vi này không được chấp nhận đối với ứng dụng (khá đơn giản) mà tôi cần. Tôi chỉ cần một kết nối TCP không liên tục. Do đó, có tài sản NetworkStream.DataAvailable là tối quan trọng đối với việc triển khai của tôi.

Các giải pháp hiện tại:

tôi đã kết thúc viết một giao diện và một wrapper để NetworkStream. Tôi cũng đã kết thúc mảng byte cho bộ đệm nhận tạm thời vào phương thức. Đơn vị kiểm tra nó bây giờ hoạt động khá tốt.

public static void ReadDataIntoBuffer(INetworkStream networkStream, Queue dataBuffer, byte[] tempRXBuffer) 
{ 
    if ((networkStream != null) && (dataBuffer != null) && (tempRXBuffer != null)) 
    { 
     // read the data from the network stream into the temporary buffer 
     while(networkStream.DataAvailable) 
     { 
      Int32 numberOfBytesRead = networkStream.Read(tempRXBuffer, 0, tempRXBuffer.Length); 

      // move all data into the main buffer 
      for (Int32 i = 0; i < numberOfBytesRead; i++) 
      { 
       dataBuffer.Enqueue(tempRXBuffer[i]); 
      } 
     } 
    } 
    else ... 
} 

Và đây là bài kiểm tra đơn vị mà tôi sử dụng:

public void TestReadDataIntoBuffer() 
{ 
    var networkStreamMock = new Mock<INetworkStream>(); 
    StringBuilder sb = new StringBuilder(); 

    sb.Append(_testMessageConstant1); 
    sb.Append(_testMessageConstant2); 
    sb.Append(_testMessageConstant3); 
    sb.Append(_testMessageConstant4); 
    sb.Append(_testMessageConstant5); 


    // ARRANGE 
    byte[] tempRXBuffer = Encoding.UTF8.GetBytes(sb.ToString()); 

    // return true so that the call to Read() is made 
    networkStreamMock.Setup(x => x.DataAvailable).Returns(true); 

    networkStreamMock.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Callback(() => 
     { 
      // after the call to Read() re-setup the property so that we 
      // we exit the data reading loop again 
      networkStreamMock.Setup(x => x.DataAvailable).Returns(false); 

     }).Returns(tempRXBuffer.Length); 

    Queue resultQueue = new Queue(); 

    // ACT 
    ReadDataIntoBuffer(networkStreamMock.Object, resultQueue, tempRXBuffer); 

    // ASSERT 
    Assert.AreEqual(Encoding.UTF8.GetBytes(sb.ToString()), resultQueue.ToArray()); 
} 
+2

Bạn có thể thay đổi 'NetworkStream' thành một' Stream' không? Điều đó có thể làm mọi thứ dễ dàng hơn. –

+0

Tôi đoán tôi có thể làm điều đó. Bạn có thể giải thích cách đó sẽ làm mọi thứ dễ dàng hơn không? Moq có thể giả lập một đối tượng Stream không? –

+0

có, Moq có thể giả lập một luồng vì nó là một lớp trừu tượng, tuy nhiên 'Dòng' không chứa thuộc tính' DataAvailable', vì vậy tôi đề nghị bạn sử dụng cách tiếp cận mà tôi đã nêu bên dưới để thay thế. –

Trả lời

15

Bạn không thể giả mạo NetworkStream bằng moq vì đây không phải là lớp trừu tượng hoặc giao diện. Tuy nhiên, bạn có thể tạo một trừu tượng trên đầu trang của nó và thay đổi phương thức của bạn để chấp nhận một thể hiện của trừu tượng đó.Nó có thể là một cái gì đó như thế này:

public interface IMyNetworkStream 
{ 
    int Read([In, Out] byte[] buffer, int offset, int size); 
    bool DataAvailable {get;} 
} 

Bây giờ bạn tạo ra một lớp mà thực hiện giao diện:

public class MyNetworkStream : IMyNetworkStream 
{ 
    private NetworkStream stream; 

    public MyNetworkStream(NetworkStream ns) 
    { 
     if(ns == null) throw new ArgumentNullException("ns"); 
     this.stream = ns; 
    } 

    public bool DataAvailable 
    { 
     get 
     { 
      return this.stream.DataAvailable; 
     } 
    } 

    public int Read([In, Out] byte[] buffer, int offset, int size) 
    { 
     return this.stream.Read(buffer, offset, size); 
    } 

} 

Bây giờ bạn có thể thay đổi phương pháp chữ ký của bạn để sử dụng một thể hiện của IMyNetworkStream và sử dụng Moq để tạo ra một mô hình IMyNetworkStream.

+0

Cảm ơn bạn đã xây dựng! Tôi nghĩ rằng tôi có thể tránh được việc phải sử dụng thuộc tính DataAvailable của NetworkStream bằng cách kiểm tra kết quả của việc gọi Read(). Hoặc bạn có thể thấy lý do tại sao điều đó không nên hoạt động? –

+0

Không, điều đó có thể hoạt động tốt. Tuy nhiên, việc thực hiện 'DataAvailable' có một chút khác biệt. Bạn có thể muốn xem xét việc thực hiện trong phản xạ nếu nghi ngờ. –

+0

Tôi sẽ kiểm tra NetworkStream.DataAvailable trước khi tôi thực sự gọi phương thức ReadDataIntoBuffer() của mình. Bằng cách đó tôi có thể vượt qua chỉ là đối tượng Stream. Cảm ơn phản hồi của bạn! –

1

Đặt NetworkStream đằng sau một giao diện đơn giản (chỉ với các cuộc gọi mà bạn cần) và nhạo báng đó.

+0

Đó là một đề xuất hợp lệ. Tôi đã tự hỏi nếu tôi bằng cách nào đó có thể là một chút lười biếng hơn và bỏ qua việc tạo ra một giao diện mà tôi có thể giả ... –

6

Như được đề xuất trong nhận xét - bạn có thể thay đổi loại thành Luồng để bạn có thể vượt qua MemoryStream không?

+0

Vâng đó là một gợi ý tốt! Tôi sẽ không thể sử dụng NetworkStream.DataAvailable nữa như trong mã ví dụ của tôi mặc dù. Tôi đoán tôi có thể thay thế việc thực hiện đọc dữ liệu thực tế bằng cách nào đó. –

Các vấn đề liên quan