2012-02-15 3 views
2

Я хочу разработать тестируемую TcpClient/TcpListener упаковку. Я хочу быть в состоянии издеваться над входящими и исходящими данными.Как я могу разработать тестируемый TcpClient/TcpListener Wrapper

Я хочу сделать это, потому что у меня есть компоненты более высокого уровня, которые должны реагировать на сетевые сообщения. По причинам тестирования я хочу издеваться над ними (сети).

Может кто-нибудь, пожалуйста, дайте мне удар в правильном направлении?

ответ

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

Это не имеет значения, если вы используете Socket, TcpClient или NetworkStream.

Update, как писать тесты

Вот некоторые примеры испытаний с использованием текучих утверждений и 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 и даже поднять аргументы событий. – IAbstract

Смежные вопросы