Этот вопрос размещен как продолжение Как вы имитируете класс для модульного теста, у которого есть возвращаемый тип, но нет входных параметров
Задав исходный вопрос, я создал минимальный, полный и проверяемый пример, который используется в качестве основы для этого вопроса.
У меня есть контроллер (показан ниже)
public class HomeController : Controller
{
private OrganisationLogic _organisationLogic;
public HomeController(OrganisationLogic logic)
{
_organisationLogic = new OrganisationLogic();
}
public ActionResult Index()
{
var model = _organisationLogic.GetOrganisation().ToViewModel();
return View(model);
}
}
Контроллер извлекает данные из метода на уровне бизнес-логики под названием OrganisationLogic (показано ниже).
public class OrganisationLogic : LogicRepository<OrganisationModel>
{
public OrganisationLogic()
{
}
public override OrganisationModel GetOrganisation()
{
return new OrganisationModel { Id = 1, OrganisationName = "My Orgaisation", Address = "Logic" };
}
}
Бизнес-логика позже наследует репозиторий Logic (показано ниже)
public abstract class LogicRepository<T> : ILogicRepository<T>
{
protected LogicRepository()
{
}
public abstract T GetOrganisation();
}
Логический репозиторий реализует интерфейс ILogicRepository (показан ниже)
public interface ILogicRepository<TModel>
{
TModel GetOrganisation();
}
Я хочу провести модульное тестирование HomeController, чтобы убедиться, что данные, отображаемые в ViewModel, правильно возвращаются из OrganisationLogic и преобразуются из OrganisationModel в OrganisationViewModel.
Я написал следующий UnitTest, который использует Moq для имитации метода _OrganisationLogic.GetOrganisation ().
[TestMethod]
public void Index()
{
var _OrganisationLogic = new Mock<OrganisationLogic>();
var testdata = new OrganisationModel { Id = 3, OrganisationName = "My Test Class Organisation" };
_OrganisationLogic.Setup(p => p.GetOrganisation()).Returns(testdata).Callback<OrganisationModel>(p=>p = testdata);
HomeController controller = new HomeController(_OrganisationLogic.Object);
ViewResult result = controller.Index() as ViewResult;
OrganisationViewModel model = (OrganisationViewModel)result.Model;
Assert.AreEqual(testdata.OrganisationName,model.OrganisationName);
}
Когда я запускаю тест, он не проходит. Причина этого в том, что Mock не переопределил класс, а вместо этого вернул результат фактического метода на уровне BusinessLogic.
В моем исходном вопросе я написал, что генерируемое сообщение об ошибке было:
System.ArgumentException Недопустимый обратный вызов. Настройка метода с 0 параметром (ами) не может вызывать обратный вызов с другим количеством параметров (1). Источник = Moq StackTrace: в Moq.MethodCallReturn2.ValidateNumberOfCallbackParameters (MethodInfo callbackMethod) в Moq.MethodCallReturn2.ValidateReturnDelegate (обратный вызов делегата) в Moq.MethodCallReturn2.Returns [T] (Func2 valueExpression)
Теперь я смог точно воспроизвести это сообщение об ошибке, выполнив следующий модульный тест. Я подозреваю, что приведенный выше модульный тест ближе к тому, что мне нужно, и что в приведенном ниже примере я неправильно настраиваю Return (). Мысли по этому поводу приветствуются?
[TestMethod]
public void Index()
{
var _OrganisationLogic = new Mock<OrganisationLogic>();
var testdata = new OrganisationModel { Id = 3, OrganisationName = "My Test Class Organisation" };
_OrganisationLogic.Setup(p => p.GetOrganisation()).Returns<OrganisationModel>(p=>p = testdata).Callback<OrganisationModel>(p=>p = testdata);
HomeController controller = new HomeController(_OrganisationLogic.Object);
ViewResult result = controller.Index() as ViewResult;
OrganisationViewModel model = (OrganisationViewModel)result.Model;
Assert.AreEqual(testdata.OrganisationName,model.OrganisationName);
}
Мой вопрос в том, как настроить Mock, чтобы он использовал мои тестовые данные.
Чтобы помочь с ответом на вышеуказанный вопрос, я разместил на GitHub версию кода, которая демонстрирует эту проблему и показывает, что тест не проходит. Доступ к нему можно получить по адресу https://github.com/stephenwestgarth/UnitTestExample.
Любая помощь будет очень высоко ценится.





Измените конструктор HomeController так, чтобы тип параметра был ILogicRepository<OrganisationModel>, и для поля также потребуется этот тип, и используйте экземпляр, который был введен в конструктор. _organisationLogic = logic; (ваш код выше игнорирует параметр и создает свой собственный конкретный экземпляр OrganisationLogic, что означает, что он не использует ваш фиктивный объект.
В тесте измените объявление _OrganisationLogic на ...
var _OrganisationLogic = new Mock<ILogicRepository<OrganisationModel>>();
Как я уже сказал, когда вы спросили ранее, я не думаю, что вам нужен этот обратный вызов.
Отредактированный конструктор будет выглядеть так ...
private ILogicRepository<OrganisationModel> _organisationLogic;
public HomeController(ILogicRepository<OrganisationModel> logic)
{
_organisationLogic = logic;
}
Я отключил обратный вызов и получаю ту же ошибку
Да, потому что он не работает в части Returns этого кода, прежде чем он попадет в обратный вызов.
Идеально! Тестовые прохождения! Спасибо за вашу помощь! Для всех, кто приходит после того, как я передал рабочую версию в git!
@SteveWestgarth Поздравляю, и молодец, приложив усилия, чтобы создать MCVE.
Спасибо - честно говоря, я бы продвинулся намного быстрее, если бы я создал MCVE в первую очередь - мне потребовалось 2 часа сегодня вечером, чтобы сделать MCVE и исправить проблему в исходном проекте; до сегодняшнего вечера я потратил 3 дня, пытаясь решить проблему. Это просто показывает, что небольшие усилия действительно окупаются!
Не могли бы вы изменить мой конструктор выше и опубликовать готовый конструктор? Я изо всех сил пытаюсь понять все ваше предложение :)