Настройка Moq, вызывающая null

#c# #unit-testing #nunit #moq

#c# #модульное тестирование #nunit #moq

Вопрос:

Я новичок в модульном тестировании и Moq, поэтому, если я совершенно не прав в своем методе или понимании, пожалуйста, помогите мне.

У меня есть логический метод, который я тестирую. Я прокомментировал логику, но все, что она делает, это проверяет несколько значений в ‘model’ и возвращает, если есть проблема. В рассматриваемом нами случае проблем нет.

         public ReplyDto SaveSettings(SnowballDto model)
        {

            // Some logic here that reads from the model.

            var result = _data.SaveSettings(model);
            return resu<
        }
  

Мой тест с использованием NUnit и MOQ выглядит следующим образом:

 _logic = new SnowballLogic(mockSnowballData.Object, mockLog.Object);

           mockSnowballData
                .Setup(x => x.SaveSettings(SnowballDto_Good))
                .Returns(new ReplyDto {
                    IsSuccess = true,
                    Message = "Saved",
                    ReplyKeyID = 1
                });
  

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

  private void SetupData()
        {
            SnowballDto_Good = new SnowballDto {
                FirstPaymentDate = DateTime.UtcNow,
                ID = 1,
                OrderedIDPriority = new List<int>(),
                SnowballTypeID = 1,
                TargetPayment = 1000
            };

            DebtDtoList_ThreeDebt.Clear();
            DebtDtoList_ThreeDebt.Add(new DebtDto { ID = 1, Description = "Debt 1", ManualSnowballPriority = 1, MinimumMonthlyPaymentAmount = 140, OpeningBalance = 5000, RunningData = new DebtRunningDto { Balance = 5000 }, OpeningDate = DateTime.UtcNow, SnowballID = 1, StandardRate = 10 });
            DebtDtoList_ThreeDebt.Add(new DebtDto { ID = 2, Description = "Debt 2", ManualSnowballPriority = 2, MinimumMonthlyPaymentAmount = 90, OpeningBalance = 1600, RunningData = new DebtRunningDto { Balance = 1600 }, OpeningDate = DateTime.UtcNow, SnowballID = 1, StandardRate = 15 });
            DebtDtoList_ThreeDebt.Add(new DebtDto { ID = 3, Description = "Debt 3", ManualSnowballPriority = 3, MinimumMonthlyPaymentAmount = 300, OpeningBalance = 9000, RunningData = new DebtRunningDto { Balance = 9000 }, OpeningDate = DateTime.UtcNow, SnowballID = 1, StandardRate = 20 });


        }
  

Итак, мое понимание MOQ заключается в том, что я говорю: «Когда вызывается метод «saveSettings» класса SnowballData и передается объект «SnowballDto_Good», всегда возвращайте новый ReplyDto с isSuccess = true.

Поэтому, когда я выполняю вызов:

 var result = _data.SaveSettings(model);
  

Он должен возвращать ReplyDto с isSuccess = true

Однако, когда я устанавливаю точку останова при вызове ‘saveSettings’, она все время возвращает null.

Если я изменю свою настройку на:

 .Setup(x => x.SaveSettings(It.IsAny<SnowballDto>()))
  

Тест пройден. Почему он возвращает null, когда я даю ему настоящий SnowballDto?

Комментарии:

1. Он сравнивает аргумент по ссылке

2. Вы передаете тот же объект, который использовался в программе установки?

3. Мы должны увидеть больше. Где создается SnowballDto_Good экземпляр. SnowballDto Переопределяет Equals(object) ли тип, если да, то как? Кроме того, покажите достаточно кода, чтобы убедить нас, что это тот же самый экземпляр SnowballDto . Помните семантику замыкания, когда лямбда в Setup превращается в дерево выражений.

4.Но одно new SnowballDto { FirstPaymentDate = DateTime.UtcNow, ID = 1, OrderedIDPriority = new List<int>(), SnowballTypeID = 1, TargetPayment = 1000 } равно другому new SnowballDto { FirstPaymentDate = DateTime.UtcNow, ID = 1, OrderedIDPriority = new List<int>(), SnowballTypeID = 1, TargetPayment = 1000 } ? Ответ зависит от того, SnowballDto переопределяет Equals(object) ли класс и каким образом. Наиболее вероятное объяснение заключается в том, что настройка не «выполнена», поскольку аргумент не считается совпадающим. Свободный макет просто вернет, null если настройки не совпадают.

5. Кроме того, два чтения DateTime.UtcNow не дадут одного и того же значения. Проходит время; при следующем запросе «сейчас», возможно, прошло несколько микросекунд, и «сейчас» будет изменено.

Ответ №1:

что ж, похоже, вы передаете экземпляр SnowballDto named model в части «act» вашего теста

var result = _data.SaveSettings(model);

Однако при настройке moq вы настраиваете его на возврат нового, ReplyDto только когда для SnowballDto_Good экземпляра указано значение SaveSettings . Во всех других случаях макет метода не настроен, и — в случае свободной стратегии редактирования (по умолчанию) — он вернет значение по умолчанию для SaveSettings возвращаемого типа. В данном случае: null.

При использовании It.IsAny<SnowballDto> вы в основном указываете moq настроить SaveSettings возврат нового экземпляра не только тогда, когда ему передается экземпляр SnowballDto_Good , но и любой экземпляр этого типа.

Что вам нужно сделать, так это изменить вашу «действующую» часть вашего теста следующим образом:

var result = _data.SaveSettings(SnowballDto_Good);

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

Именно поэтому мне нравится создавать экземпляры моих mocks с использованием MockBehavior.Строгий.

Вместо возврата null, она выдаст исключение, которое сообщит вам, что вы неправильно настроили свой макет.

Надеюсь, это поможет.