Как инверсия зависимостей помогает в тестировании и почему нельзя замокать класс напрямую
Что проверяют
Понимаете ли вы DIP не как цитату из SOLID, а как инженерный приём: зависимость от абстракции делает класс тестируемым в изоляции. Вторая половина вопроса проверяет, знаете ли вы, как устроены Moq и NSubstitute внутри. Следом часто просят показать, как тестировать код, который зовёт DateTime.Now или HttpClient.
Как отвечать
Принцип: модули верхнего уровня зависят от абстракций, а конкретную реализацию им передают снаружи, обычно через конструктор и контейнер Microsoft.Extensions.DependencyInjection. Тогда OrderService получает IPaymentGateway, в тесте вместо него подставляется мок, и тест проверяет логику сервиса без сети и базы.
Почему класс напрямую не мокается:
- как работают мок-библиотеки: Moq и NSubstitute через Castle DynamicProxy генерируют в рантайме наследника и переопределяют члены. Переопределить можно только
virtual,abstractили член интерфейса; - невиртуальные методы: у мока класса они выполняют настоящий код, а
Setupв Moq на таком методе падает сNotSupportedException; sealed,staticи методы расширения: наследника не создать, перехватывать нечего;- конструктор: прокси класса вызывает базовый конструктор, поэтому его побочные эффекты и обязательные аргументы достаются тесту.
Отсюда практика: выделять интерфейс там, где проходит граница с внешним миром (время, файлы, HTTP, база). Для времени в .NET 8 появился встроенный TimeProvider, для HTTP подменяют HttpMessageHandler. Инструменты на профилировщике CLR умеют перехватывать и невиртуальные методы, но это тяжёлая артиллерия для легаси, а не замена нормальной архитектуре.
Где спотыкаются
Путают DIP с DI-контейнером. Контейнер лишь собирает граф объектов, а принцип говорит о направлении зависимостей и работает даже при ручной сборке в Main.
Говорят «замокать класс невозможно в принципе». Можно, если методы виртуальные и класс не запечатан, просто тогда вы тестируете полуреальный объект и получаете хрупкие тесты.
Заводят интерфейс на каждый класс, включая DTO и чистые вычисления. Мокать стоит внешние границы и побочные эффекты, чистую логику проще проверять настоящей.