2012-02-15 56 views
2

我想开发一个可测试的TcpClient/TcpListener包装器。我希望能够模拟传入和传出的数据。我如何开发一个可测试的TcpClient/TcpListener包装器

我想这样做,因为我有更高层的组件应该对网络消息作出反应。出于测试的原因,我想模拟(网络)他们。

有人能给我一个正确的方向吗?

回答

4

您可以使用Decorator Pattern

  • 让你自己的类,它只是包装的TcpClient的
  • 该类根本就通过调用在TcpClient的功能。
  • 对这个类的重载构造函数可以接受一个实际的tcp客户端的实例,如果创建一个实例,它将包装它。
  • 提取接口,这样你的新类应实现的接口ITcpClient
  • 更新所有依赖于使用新界面ITcpClient
  • 您的新界面现在mockable,注入你的嘲弄是适当的测试路程:)

对TcpServer重复相同的操作。

6

不要模拟ITcpClient和INetworkStream。

网络层是没有什么比这更:

public interface INetworkClient : IDisposable 
{ 
    event EventHandler<ReceivedEventArgs> BufferReceived; 
    event EventHandler Disconnected; 
    void Send(byte[] buffer, int offset, int count); 
} 

public class ReceivedEventArgs : EventArgs 
{ 
    public ReceivedEventArgs(byte[] buffer) 
    { 
     if (buffer == null) throw new ArgumentNullException("buffer"); 
     Buffer = buffer; 
     Offset = 0; 
     Count = buffer.Length; 
    } 

    public byte[] Buffer { get; private set; } 
    public int Offset { get; private set; } 
    public int Count { get; private set; } 
} 

,如果您使用的是SocketTcpClientNetworkStream它不应该的问题。

更新,如何编写测试

下面是使用流利的断言和NSubstitute一些测试实例。

类被测试:

public class ReceivedMessageEventArgs : EventArgs 
{ 
    public ReceivedMessageEventArgs(string message) 
    { 
     if (message == null) throw new ArgumentNullException("message"); 
     Message = message; 
    } 

    public string Message { get; private set; } 
} 

public class SomeService 
{ 
    private readonly INetworkClient _networkClient; 
    private string _buffer; 

    public SomeService(INetworkClient networkClient) 
    { 
     if (networkClient == null) throw new ArgumentNullException("networkClient"); 
     _networkClient = networkClient; 
     _networkClient.Disconnected += OnDisconnect; 
     _networkClient.BufferReceived += OnBufferReceived; 
     Connected = true; 
    } 

    public bool Connected { get; private set; } 

    public event EventHandler<ReceivedMessageEventArgs> MessageReceived = delegate { }; 

    public void Send(string msg) 
    { 
     if (msg == null) throw new ArgumentNullException("msg"); 
     if (Connected == false) 
      throw new InvalidOperationException("Not connected"); 

     var buffer = Encoding.ASCII.GetBytes(msg + "\n"); 
     _networkClient.Send(buffer, 0, buffer.Length); 
    } 

    private void OnDisconnect(object sender, EventArgs e) 
    { 
     Connected = false; 
     _buffer = ""; 
    } 

    private void OnBufferReceived(object sender, ReceivedEventArgs e) 
    { 
     _buffer += Encoding.ASCII.GetString(e.Buffer, e.Offset, e.Count); 
     var pos = _buffer.IndexOf('\n'); 
     while (pos > -1) 
     { 
      var msg = _buffer.Substring(0, pos); 
      MessageReceived(this, new ReceivedMessageEventArgs(msg)); 

      _buffer = _buffer.Remove(0, pos + 1); 
      pos = _buffer.IndexOf('\n'); 
     } 
    } 
} 

最后测试:

[TestClass] 
public class SomeServiceTests 
{ 
    [TestMethod] 
    public void service_triggers_msg_event_when_a_complete_message_is_recieved() 
    { 
     var client = Substitute.For<INetworkClient>(); 
     var expected = "Hello world"; 
     var e = new ReceivedEventArgs(Encoding.ASCII.GetBytes(expected + "\n")); 
     var actual = ""; 

     var sut = new SomeService(client); 
     sut.MessageReceived += (sender, args) => actual = args.Message; 
     client.BufferReceived += Raise.EventWith(e); 

     actual.Should().Be(expected); 
    } 

    [TestMethod] 
    public void Send_should_invoke_Send_of_networkclient() 
    { 
     var client = Substitute.For<INetworkClient>(); 
     var msg = "Hello world"; 

     var sut = new SomeService(client); 
     sut.Send(msg); 

     client.Received().Send(Arg.Any<byte[]>(), 0, msg.Length + 1); 
    } 

    [TestMethod] 
    public void Send_is_not_allowed_while_disconnected() 
    { 
     var client = Substitute.For<INetworkClient>(); 
     var msg = "Hello world"; 

     var sut = new SomeService(client); 
     client.Disconnected += Raise.Event(); 
     Action actual =() => sut.Send(msg); 

     actual.ShouldThrow<InvalidOperationException>(); 
    } 
} 
+0

我不明白...你会如何使用此代码? – Grrbrr404 2012-02-15 12:44:07

+0

您需要创建一个类,它在内部使用'TcpClient'或'Socket'并使其实现该接口。 – jgauffin 2012-02-15 12:59:25

+0

有了这个接口,你可以使用MoQ,甚至引发事件参数。 – IAbstract 2014-01-13 17:25:37

相关问题