2012-02-15 5 views
2

私はテスト可能なTcpClient/TcpListenerラッパーを開発したいと考えています。私は入ってくるデータと出て行くデータをモックできるようにしたい。テスト可能なTcpClient/TcpListenerラッパーを開発するには

私はネットワークメッセージで反応する必要がある上位コンポーネントがあるため、これを実行したいと考えています。テストの理由から、私は(ネットワーク)を模擬したい。

いくつかの人が私に正しい方向にキックを与えてくれますか?

答えて

4

あなたは、このクラスは、単にれるtcpClientで関数の呼び出しを通過んばかりれるtcpClient

  • をラップ非常に独自のクラスを作成しますDecorator Pattern

    • を使用することができます。
    • このクラスへのオーバーロードされたコンストラクタは、作成中のクライアントがラップする実際のtcpクライアントのインスタンスを受け入れることができます。
    • 新しいクラスが
    • 更新し、すべての依存関係があなたのモックを注入し、
    • あなたの新しいインターフェイスは、今mockableあるITcpClient新しいインターフェイスを使用するためには適切であったと:)
    • を離れテストITcpClientインターフェイスを実装する必要がありますので、インターフェースを抽出

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

    あなたがSocketTcpClientまたはNetworkStreamを使用している場合、それは問題ではないはず。

    アップデートは、テスト

    を作成する方法をここで流暢アサーションと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

    +0

    'TcpClient'または' Socket'を内部的に使用するクラスを作成し、そのインターフェースを実装する必要があります。 – jgauffin

    +0

    このインターフェイスを使用すると、MoQを使用してイベントargsを発生させることさえできます。 – IAbstract

    関連する問題