Как создать класс для модульного тестирования

Этот вопрос размещен как продолжение Как вы имитируете класс для модульного теста, у которого есть возвращаемый тип, но нет входных параметров

Задав исходный вопрос, я создал минимальный, полный и проверяемый пример, который используется в качестве основы для этого вопроса.

У меня есть контроллер (показан ниже)

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.

Любая помощь будет очень высоко ценится.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Измените конструктор HomeController так, чтобы тип параметра был ILogicRepository<OrganisationModel>, и для поля также потребуется этот тип, и используйте экземпляр, который был введен в конструктор. _organisationLogic = logic; (ваш код выше игнорирует параметр и создает свой собственный конкретный экземпляр OrganisationLogic, что означает, что он не использует ваш фиктивный объект.

В тесте измените объявление _OrganisationLogic на ... var _OrganisationLogic = new Mock<ILogicRepository<OrganisationModel>>();

Как я уже сказал, когда вы спросили ранее, я не думаю, что вам нужен этот обратный вызов.

Отредактированный конструктор будет выглядеть так ...

private ILogicRepository<OrganisationModel> _organisationLogic;

public HomeController(ILogicRepository<OrganisationModel> logic)
{
   _organisationLogic = logic;
}

Не могли бы вы изменить мой конструктор выше и опубликовать готовый конструктор? Я изо всех сил пытаюсь понять все ваше предложение :)

Steve Westgarth 12.03.2018 22:38

Я отключил обратный вызов и получаю ту же ошибку

Steve Westgarth 12.03.2018 22:40

Да, потому что он не работает в части Returns этого кода, прежде чем он попадет в обратный вызов.

Richardissimo 12.03.2018 22:41

Идеально! Тестовые прохождения! Спасибо за вашу помощь! Для всех, кто приходит после того, как я передал рабочую версию в git!

Steve Westgarth 12.03.2018 22:44

@SteveWestgarth Поздравляю, и молодец, приложив усилия, чтобы создать MCVE.

Richardissimo 12.03.2018 22:58

Спасибо - честно говоря, я бы продвинулся намного быстрее, если бы я создал MCVE в первую очередь - мне потребовалось 2 часа сегодня вечером, чтобы сделать MCVE и исправить проблему в исходном проекте; до сегодняшнего вечера я потратил 3 дня, пытаясь решить проблему. Это просто показывает, что небольшие усилия действительно окупаются!

Steve Westgarth 12.03.2018 23:46

Другие вопросы по теме