Предполагая интерфейс IReader, реализацию интерфейса IReader ReaderImplementation и класс ReaderConsumer, который потребляет и обрабатывает данные из считывателя.
public interface IReader
{
object Read()
}
Реализация
public class ReaderImplementation
{
...
public object Read()
{
...
}
}
Потребитель:
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
}
}
Для тестирования ReaderConsumer и обработки я использую макет IReader. Так ReaderConsumer становится:
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
}
}
}
В этом решении mocking вводит предложение if для производственного кода, поскольку только конструктор mocking предоставляет экземпляры интерфейса.
Во время написания этого я понимаю, что блок try-finally несколько не связан, так как он предназначен для того, чтобы обрабатывать изменение пользователем местоположения во время выполнения приложения.
В целом он чувствует себя вонючим, как с ним можно справиться лучше?
mocking
code-smell
Кристиан Мо
источник
источник
ReaderConsumer
независимостиReaderImplementation
?Ответы:
Вместо инициализации читателя из вашего метода, переместите эту строку
В конструктор по умолчанию без параметров.
Не существует такого понятия, как «фиктивный конструктор», если у вашего класса есть зависимость, которая требуется ему для работы, тогда конструктору следует либо предоставить эту вещь, либо создать ее.
источник
Вам нужен только один конструктор:
в вашем производственном коде:
в вашем тесте:
источник
Посмотрите на внедрение зависимостей и инверсию контроля
И Ewan, и RubberDuck имеют отличные ответы. Но я хотел бы упомянуть еще одну область, в которую нужно обратить внимание, это внедрение зависимостей (DI) и инверсия контроля (IoC). Оба этих подхода переносят проблему, с которой вы сталкиваетесь, в фреймворк / библиотеку, так что вам не нужно беспокоиться об этом.
Ваш пример прост и быстро обходится без него, но вы неизбежно будете опираться на него, и в итоге вы получите либо тонны конструкторов, либо процедуры инициализации, которые выглядят так:
var foo = new Foo (new Bar (новый Baz (), новый Quz ()), новый Foo2 ());
С DI / IoC вы используете библиотеку, которая позволяет вам прописать правила для сопоставления интерфейсов с реализациями, а затем вы просто говорите «Дайте мне одурачивание», и это работает, как соединить все это.
Есть много очень дружественных IoC-контейнеров (как их называют), и я собираюсь порекомендовать один из них, но, пожалуйста, изучите, так как есть очень много хороших вариантов.
Простой для начала это:
http://www.ninject.org/
Вот список для изучения:
http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx
источник