#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, она выдаст исключение, которое сообщит вам, что вы неправильно настроили свой макет.
Надеюсь, это поможет.