Как прокидывать зависимости при написании тестов
Что проверяют
Умеете ли вы писать код, который тестируется без подъёма всего приложения, и отличаете ли моки от стабов и фейков. Следом часто спрашивают, что делать с DateTime.Now, HttpClient и базой.
Как отвечать
Главная мысль: если зависимости приходят через конструктор, в юнит-тесте контейнер не нужен. Вы пишете new OrderService(fakeRepo, timeProvider) и контролируете всё, что видит класс.
- виды двойников: стаб отдаёт заготовленные ответы, мок проверяет, что вызов был, фейк это рабочая упрощённая реализация, например репозиторий на
List<T>. Библиотеки: Moq, NSubstitute, FakeItEasy. Они подменяют интерфейсы иvirtual-члены, статику иsealed-классы не мокают; - время: вместо
DateTime.NowвнедряйтеTimeProviderиз .NET 8, в тестах беритеFakeTimeProviderи двигайте время вручную; - HTTP:
HttpClientнапрямую не мокают. ПодменяютHttpMessageHandlerили выносят вызовы в свой интерфейс-клиент; - логи:
NullLogger<T>.Instance, а если важно, что записалось,FakeLogger<T>; - база: SQLite в памяти или Testcontainers с настоящим PostgreSQL. Провайдер EF Core InMemory сама Microsoft для тестов не советует: он не проверяет ограничения и не поддерживает транзакции;
- тесты API:
WebApplicationFactory<Program>иConfigureTestServices, где отдельные регистрации заменяются на фейки.
Пример на NSubstitute: repo.GetById(5).Returns(order), затем await repo.Received(1).SaveAsync(order). В NUnit 4 классические Assert.AreEqual переехали в ClassicAssert, основной стиль теперь Assert.That(result, Is.EqualTo(10)).
Где спотыкаются
Мокают всё подряд, включая DTO и простые классы без поведения. Тест начинает проверять порядок вызовов вместо результата и ломается при любом рефакторинге.
Собирают в юнит-тесте настоящий ServiceCollection. Это медленно и прячет лишние зависимости, которые при ручном создании объекта сразу бросились бы в глаза.
Оставляют в коде DateTime.Now, File и статические хелперы. Такой класс не проверить без реальных часов и диска, и тест начинает падать в полночь или в последний день месяца.