Zakładając interfejs IReader, implementację interfejsu IReader ReaderImplementation oraz klasę ReaderConsumer, która zużywa i przetwarza dane z czytnika.
public interface IReader
{
object Read()
}
Realizacja
public class ReaderImplementation
{
...
public object Read()
{
...
}
}
Konsument:
public class ReaderConsumer()
{
public string location
// constructor
public ReaderConsumer()
{
...
}
// read some data
public object ReadData()
{
IReader reader = new ReaderImplementation(this.location)
data = reader.Read()
...
return processedData
}
}
Do testowania ReaderConsumer i przetwarzania używam makiety IReadera. Czytnik ReaderConsumer staje się:
public class ReaderConsumer()
{
private IReader reader = null
public string location
// constructor
public ReaderConsumer()
{
...
}
// mock constructor
public ReaderConsumer(IReader reader)
{
this.reader = reader
}
// read some data
public object ReadData()
{
try
{
if(this.reader == null)
{
this.reader = new ReaderImplementation(this.location)
}
data = reader.Read()
...
return processedData
}
finally
{
this.reader = null
}
}
}
W tym rozwiązaniu kpina wprowadza zdanie produkcyjne do kodu produkcyjnego, ponieważ tylko konstrukt kpiąca dostarcza instancje interfejsu.
Pisząc to, zdaję sobie sprawę, że blok try-last jest w pewnym stopniu niezwiązany, ponieważ ma za zadanie obsłużyć użytkownika zmieniającego lokalizację podczas działania aplikacji.
Ogólnie wydaje się śmierdzący, jak można sobie z tym poradzić lepiej?
mocking
code-smell
Kristian Mo
źródło
źródło
ReaderConsumer
niezależnymReaderImplementation
?Odpowiedzi:
Zamiast inicjować czytnik z twojej metody, przenieś tę linię
Do domyślnego konstruktora bez parametrów.
Nie ma czegoś takiego jak „próbny konstruktor”, jeśli twoja klasa ma zależność, której wymaga do działania, to konstruktor powinien albo dostarczyć tę rzecz, albo ją stworzyć.
źródło
Potrzebujesz tylko jednego konstruktora:
w kodzie produkcyjnym:
w teście:
źródło
Spójrz na wstrzykiwanie zależności i odwracanie kontroli
Zarówno Ewan, jak i RubberDuck mają doskonałe odpowiedzi. Chciałem jednak wspomnieć o innym obszarze do zbadania, w którym jest wstrzykiwanie zależności (DI) i odwracanie kontroli (IoC). Oba te podejścia przenoszą napotkany problem na platformę / bibliotekę, abyś nie musiał się tym martwić.
Twój przykład jest prosty i można go szybko pominąć, ale nieuchronnie zamierzasz na nim budować i skończysz z mnóstwem konstruktorów lub procedurami inicjalizacji, które wyglądają jak:
var foo = nowy Foo (nowy pasek (nowy Baz (), nowy Quz ()), nowy Foo2 ());
Z DI / IoC korzystasz z biblioteki, która pozwala ci przeliterować zasady dopasowywania interfejsów do implementacji, a następnie po prostu mówisz „Daj mi Foo” i to wyjaśnia, jak to wszystko połączyć.
Istnieje wiele bardzo przyjaznych kontenerów IoC (jak się je nazywa) i zamierzam polecić jeden z nich, ale spójrz, ponieważ jest bardzo wiele dobrych opcji.
Prosty na początek to:
http://www.ninject.org/
Oto lista do odkrycia:
http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx
źródło