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ść?
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ść?
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();
}
}
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 ...
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). –
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ę.
To jest idealne! Dzięki! –
+1 objaśnienie plus ładny prosty przykład – Ahmad
dzięki @funkymushroom, poprawiony –