Сначала позвольте мне представить реализацию без внедрения зависимостей (что нарушит принцип инверсии зависимостей):
public class MyValidator
{
private readonly IChecksumGenerator _checksumGenerator;
public MyValidator()
{
_checksumGenerator = new MyChecksumGenerator();
}
...
}
Чтобы сделать этот код тестируемым, добавьте IChecksumGenerator:
public class MyValidator
{
private readonly IChecksumGenerator _checksumGenerator;
public MyValidator(IChecksumGenerator checksumGenerator)
{
_checksumGenerator = checksumGenerator;
}
...
}
Теперь мы можем легко протестировать MyValidator и заглушку контрольной суммы Generator, если требуется. Но реализация MyValidator алгоритмически связана с конкретной реализацией IChecksumGenerator (она просто не будет работать с любой другой реализацией). Таким образом, появляются некоторые проблемы:
- Мы представляем возможность того, что будет введен неверный IChecksumGenerator (например, из-за ошибки конфигурации контейнера IoC)
- Мы нарушаем инкапсуляцию, поскольку частная реализация MyValidator (связывание с MyChecksumGenerator) выходит за пределы класса
Лучшее решение, к которому я пришел, это следующее:
public class MyValidator
{
private readonly IChecksumGenerator _checksumGenerator;
public MyValidator()
{
_checksumGenerator = new MyChecksumGenerator;
}
internal MyValidator(IChecksumValidator checksumValidator)
{
_checksumValidator = checksumValidator;
}
...
}
Здесь я представляю специальный конструктор для целей тестирования (чтобы я мог заглушить IChecksumValidator в тестах), но открытый конструктор создает ту реализацию, с которой он связан (поэтому инкапсуляция не нарушается). Немного некрасиво создавать некоторый код для целей тестирования, но, похоже, в этом случае это имеет смысл.
Как бы вы решили эту проблему?