Ответ
Поведение объектов-заглушек (моков и стабов) задаётся в коде теста с помощью методов настройки, предоставляемых библиотеками для мокинга, такими как Moq, NSubstitute или FakeItEasy.
Основные места и способы:
- В методе
Setup()(или его аналоге) для определения того, какой метод или свойство будет подменяться. - Цепочкой методов, следующих за
Setup(), таких как:.Returns(value)— чтобы заглушка возвращала конкретное значение..Throws(exception)— чтобы заглушка выбрасывала исключение..Callback()— чтобы выполнить дополнительное действие при вызове.
Пример настройки мока с Moq:
// Создаём мок интерфейса IRepository
var mockRepository = new Mock<IRepository>();
// Задаём поведение для метода GetById
// Он будет возвращать тестовый объект при любом целочисленном аргументе
mockRepository
.Setup(repo => repo.GetById(It.IsAny<int>()))
.Returns(new Product { Id = 42, Name = "Test Product" });
// Используем мок в тестируемом сервисе
var service = new ProductService(mockRepository.Object);
var result = service.GetProductDetails(42);
// Проверяем, что сервис корректно обработал данные от заглушки
Assert.Equal("Test Product", result.Name);
// Проверяем, что метод был вызван ровно один раз
mockRepository.Verify(repo => repo.GetById(42), Times.Once);
Ключевые моменты:
It.IsAny<T>()используется для соответствия любому аргументу указанного типа.It.Is<T>(predicate)позволяет задать условие для аргумента.- Метод
Verify()используется для утверждений (assertions) о том, как и сколько раз был вызван метод мока.
Ответ 18+ 🔞
Давай разберём эту тему про моки, но без занудства, как есть. Смотри, представь, что у тебя есть какой-то сервис, который, как сука, зависит от базы данных или от другого сервиса, который тормозит как черепаха в патруле. Тебе же не охота в каждом тесте ждать, пока эта поебень ответит, да ещё и данные каждый раз подсовывать? Вот для этого и придумали заглушки, или, как их ещё называют, моки и стабы.
Всё это безобразие настраивается прямо в коде теста. Библиотеки для этого есть — Moq, NSubstitute и прочие. Суть в том, что ты берёшь интерфейс или виртуальный метод и говоришь: «Слушай сюда, дружок-пирожок, когда тебя вызовут с такими-то параметрами, веди себя вот так, а не как попало».
Где это прописывается? В основном в одном месте:
-
В методе
Setup()(или каком-то его аналоге, в зависимости от библиотеки). Это как точка входа для твоего беспредела. Ты там указываешь, какой именно метод или свойство ты хочешь подменить. -
А дальше цепляешь к нему всякие штуки, чтобы описать поведение:
.Returns(значение)— чтобы заглушка просто отдала тебе заранее приготовленное значение. Типа «на, получи и отъебись»..Throws(исключение)— чтобы она, вместо ответа, выкинула тебе исключение прямо в ебало. Проверяешь, как твой код реагирует на ошибки..Callback()— это уже для извращенцев. Позволяет выполнить какой-то дополнительный код в момент вызова, например, записать, что её таки дернули.
Вот смотри, живой пример на Moq:
// Создаём мок для интерфейса репозитория. По сути, лепим муляж.
var mockRepository = new Mock<IRepository>();
// А теперь настраиваем этот муляж.
// Говорим: "Эй, метод GetById, слушай сюда! Когда тебя вызовут с ЛЮБЫМ целым числом (It.IsAny<int>())..."
mockRepository
.Setup(repo => repo.GetById(It.IsAny<int>()))
// "...ты немедленно возвращай вот этот тестовый объект, и не спорь!"
.Returns(new Product { Id = 42, Name = "Test Product" });
// Всё, подстава готова. Пихаем этот мок в наш сервис, который тестируем.
var service = new ProductService(mockRepository.Object);
var result = service.GetProductDetails(42); // Сервис думает, что работает с реальным репозиторием, а там наш подставной козёл.
// Ну и проверяем, что сервис не обосрался, а нормально обработал то, что мы ему подсунули.
Assert.Equal("Test Product", result.Name);
// А это вообще мощная штука — Verify. Мы проверяем, что метод GetById на нашем моке был вызван ровно ОДИН раз.
// Если не был или был дважды — тест провалится. Контроль, блядь, полный.
mockRepository.Verify(repo => repo.GetById(42), Times.Once);
Что тут главное запомнить:
It.IsAny<T>()— это матч-всё. Подойдёт любой аргумент такого типа.It.Is<T>(условие)— похитрее. Тут можно условие написать, например,id => id > 0.Verify()— это не просто проверка возвращаемых данных, а проверка взаимодействия. Убедиться, что твой код вообще позвал нужный метод, да ещё и нужное количество раз. Без этого — доверия ебать ноль.
Вот и вся магия. Создал муляж, настроил его поведение, подсунул в тестируемый класс и потом проверяешь, что всё прошло по сценарию. Как в театре, только вместо актёров — объекты, а ты и режиссёр, и критик в одном флаконе.