Где задаётся поведение объектов в тестах?

Ответ

Поведение объектов-заглушек (моков и стабов) задаётся в коде теста с помощью методов настройки, предоставляемых библиотеками для мокинга, такими как Moq, NSubstitute или FakeItEasy.

Основные места и способы:

  1. В методе Setup() (или его аналоге) для определения того, какой метод или свойство будет подменяться.
  2. Цепочкой методов, следующих за 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 и прочие. Суть в том, что ты берёшь интерфейс или виртуальный метод и говоришь: «Слушай сюда, дружок-пирожок, когда тебя вызовут с такими-то параметрами, веди себя вот так, а не как попало».

Где это прописывается? В основном в одном месте:

  1. В методе Setup() (или каком-то его аналоге, в зависимости от библиотеки). Это как точка входа для твоего беспредела. Ты там указываешь, какой именно метод или свойство ты хочешь подменить.

  2. А дальше цепляешь к нему всякие штуки, чтобы описать поведение:

    • .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() — это не просто проверка возвращаемых данных, а проверка взаимодействия. Убедиться, что твой код вообще позвал нужный метод, да ещё и нужное количество раз. Без этого — доверия ебать ноль.

Вот и вся магия. Создал муляж, настроил его поведение, подсунул в тестируемый класс и потом проверяешь, что всё прошло по сценарию. Как в театре, только вместо актёров — объекты, а ты и режиссёр, и критик в одном флаконе.