2008-09-29 28 views
8

W jaki sposób ludzie podchodzą do szokujących TcpClient (lub takich rzeczy jak TcpClient)?TDD i wyśmiewanie TcpClient

Mam usługę, która pobiera TcpClient. Czy powinienem owijać to w coś jeszcze mocniejszego? Jak mam się do tego podejść?

Odpowiedz

22

Podczas zbliżania się do fałszywych klas, które nie są przyjazne dla testu (tj. Zamknięte/niewprowadzające żadnego interfejsu/metody nie są wirtualne), prawdopodobnie chciałbyś użyć wzoru projektowego Adapter.

W tym wzorze dodaje się klasę owijającą, która implementuje interfejs. Powinieneś wtedy kpić z interfejsu i upewnić się, że cały twój kod używa tego interfejsu zamiast nieprzyjaznej konkretnej klasy. Wyglądałoby to mniej więcej tak:

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(); 
    } 
} 
+0

To jest idealne! Dzięki! –

+0

+1 objaśnienie plus ładny prosty przykład – Ahmad

+0

dzięki @funkymushroom, poprawiony –

2

Stosowanie wzoru adaptera jest zdecydowanie standardowym podejściem TDD do problemu. Można jednak również po prostu utworzyć drugi koniec połączenia TCP i poprowadzić swoją wiązkę testową.

IMO powszechne stosowanie klasy adaptera zaciemnia najważniejsze elementy projektu, a także usuwa wiele elementów z testów, które naprawdę powinny być testowane w kontekście. Alternatywą jest więc zbudowanie rusztowania do testowania, aby uwzględnić więcej testowanego systemu. Jeśli budujesz swoje testy od podstaw, nadal będziesz w stanie wyizolować przyczynę niepowodzenia dla danej klasy lub funkcji, to po prostu nie będzie w izolacji ...

+0

To sprawiłoby, że test byłby testem integracyjnym, a nie testem jednostkowym. Będzie wolniejszy i bardziej prawdopodobny, że się nie powiedzie. Nie chodzi o to, że coś jest nie tak, po prostu musisz być tego świadomy (na przykład w rozwiązaniu CI). –

5

Myślę, że @ Hitchhiker jest na dobrej drodze, ale lubię też myśleć o tym, by wyodrębnić takie rzeczy, o krok dalej.

Nie wyśmiewałbym TcpClient bezpośrednio, ponieważ to nadal wiązałoby cię zbyt blisko z podstawową implementacją, nawet jeśli napisałeś testy. Oznacza to, że twoja implementacja jest ściśle powiązana z metodą TcpClient. Osobiście chciałbym spróbować coś takiego:

[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(); 
    } 

Ta implementacja będzie oddzielić cię od TCP związanych szczegóły całkowicie (jeśli jest to cel projektu), a można nawet rury na wejściu testowym z zakodowanego zbioru wartości lub plik wejściowy testu. Bardzo dobrze, jeśli jesteś w drodze do testowania kodu na dłuższą metę.