TDD и Mocking out TcpClient - PullRequest
       25

TDD и Mocking out TcpClient

9 голосов
/ 29 сентября 2008

Как люди подходят к выводу из строя TcpClient (или таких вещей, как TcpClient)?

У меня есть служба, которая принимает TcpClient. Должен ли я обернуть это во что-то более смешное? Как мне подойти к этому?

Ответы [ 3 ]

25 голосов
/ 30 сентября 2008

При переходе к фиктивным классам, которые не подходят для тестирования (т. Е. Запечатаны / не реализуют какой-либо интерфейс / методы не являются виртуальными), вы, вероятно, захотите использовать шаблон проектирования Adapter .

В этом шаблоне вы добавляете класс-оболочку, который реализует интерфейс. Затем вы должны смоделировать интерфейс и убедиться, что весь ваш код использует этот интерфейс вместо недружественного конкретного класса. Это будет выглядеть примерно так:

public interface ITcpClient
{
   Stream GetStream(); 
   // Anything you need here       
}
public class TcpClientAdapter: ITcpClient
{
   private TcpClient wrappedClient;
   public TcpClientAdapter(TcpClient client)
   {
    wrappedClient = client;
   }

   public Stream GetStream()
   {
     return wrappedClient.GetStream();
   }
}
5 голосов
/ 30 сентября 2008

Я думаю, что @Hitchhiker находится на правильном пути, но я также хотел бы подумать об абстрагировании таких вещей на шаг вперед.

Я бы не стал издеваться над TcpClient напрямую, потому что это все равно слишком тесно связывало бы вас с базовой реализацией, даже если вы писали тесты. То есть ваша реализация связана конкретно с методом TcpClient. Лично я бы попробовал что-то вроде этого:

   [Test]
    public void TestInput(){

       NetworkInputSource mockInput = mocks.CreateMock<NetworkInputSource>();
       Consumer c = new Consumer(mockInput);

       c.ReadAll();
    //   c.Read();
    //   c.ReadLine();

    }

    public class TcpClientAdapter : NetworkInputSource
    {
       private TcpClient _client;
       public string ReadAll()
       { 
           return new StreamReader(_tcpClient.GetStream()).ReadToEnd();
       }

       public string Read() { ... }
       public string ReadLine() { ... }
    }

    public interface NetworkInputSource
    {
       public string ReadAll(); 
       public string Read();
       public string ReadLine();
    }

Эта реализация полностью отделит вас от деталей, связанных с Tcp (если это является целью разработки), и вы даже можете передать данные тестового ввода из жестко закодированного набора значений или тестового входного файла. Очень хорошо, если вы находитесь на пути к тестированию своего кода на длительный период.

2 голосов
/ 30 сентября 2008

Использование шаблона адаптера, безусловно, является стандартным подходом TDD к проблеме. Однако вы также можете просто создать другой конец TCP-соединения и получить для этого тестовый жгут.

IMO, широкое использование класса адаптеров скрывает наиболее важные части проекта, а также имеет тенденцию удалять из тестирования множество вещей, которые действительно должны быть проверены в контексте. Таким образом, альтернатива состоит в том, чтобы создать ваши леса для тестирования, чтобы включить в них больше тестируемой системы. Если вы создаете свои тесты с нуля, вы все равно получите возможность изолировать причину сбоя для данного класса или функции, просто она не будет изолированной ...

...