Если я устанавливаю ожидание для фиктивного объекта, будет ли он использоваться в частных методах, которые вызывает метод, который я тестирую?
Методы:
public void MethodToTest()
{
//Do something
var result = PrivateMethod();
}
private UserProfile PrivateMethod(RegisterModel model)
{
return _unitOfWork.Repository<UserProfile>().GetSingle(u => u.UserName == model.UserName);
}
Тестовое задание:
public void Test()
{
var registerModel = new RegisterModel
{
UserName = "admin",
Password = "123456",
}
var userProfile = new UserProfile
{
UserId = 1,
UserName = "admin"
};
var unitMock = new Mock<IUnitOfWork>();
unitMock.Setup(x => x.Repository<UserProfile>().GetSingle(u => u.UserName == registerModel.UserName)).Returns(userProfile);
//Do the rest of the test
}
Это не показано здесь, но Unit of Work
поэтому вводится в конструктор _unitOfWork
можно издеваться Тот факт, что Unit of Work
используется в частном методе, означает ли это, что он все еще попадет в базу данных, даже если я смоделировал объект и ожидал метод и возвращаемое значение? Если это произойдет, как мне избежать этого? Я прочитал, что я должен извлечь свои приватные методы в отдельный класс, но он уже абстрагирован с использованием единицы работы.
2 ответа
Исправление вашей проблемы
+ Изменить
unitMock.Setup(x => x.Repository<UserProfile>().GetSingle(u => u.UserName == registerModel.UserName)).Returns(userProfile);
в
unitMock.Setup(x => x.Repository<UserProfile>().GetSingle(It.IsAny<Func<UserProfile,bool>>())).Returns(userProfile);
Лямбда-выражение, которое вы указали в Moq, может быть логически эквивалентным тому, которое вы используете в своем рабочем коде, но оно не является ссылочным эквивалентом. Это означает, что ваш GetSingle
метод вызывается, но ваше лямбда-выражение в макете не .Equals()
или же ==
к выражению, вызываемому в тестируемом модуле, поэтому функция, настроенная в Moq, никогда не вызывается. Поскольку Moq заглушает методы по умолчанию, ваш закрытый метод возвращает ноль, потому что .GetSingle(Func<T,bool>)
возвращает ноль, так как он заглушен Moq.
Почему это происходит
Вы можете увидеть это в действии, написав следующее в C# REPL:
Func<bool> func1 = () => true;
Func<bool> func2 = () => true;
Console.WriteLine(func1 == func2);
// > False
Console.WriteLine(func1.Equals(func2));
// > False
Я могу с уверенностью предположить, что вы настраиваете _unitOfWork
правильно, иначе вы бы получили NullReferenceException
, а не ваш метод, возвращающий нуль из-за цепочки метода.
Дизайн кода
Вы можете обойти это, добавив GetProfileByUsername(string)
метод в ваш репозиторий вместо того, чтобы разрешить лямбда-параметр, передаваемый методу. Прямо сейчас ваш код тоже плохо связан, поэтому я бы предложил этот подход. UnitOfWork.Repository<UserProfile>().GetSingle(..)
нарушает закон Деметры. Кроме того, ваш класс зависит от вашего IRepository<UserProfile>
, а не зависимость от вашего UnitOfWork
, Так что, действительно, вы должны насмехаться над IRepository<UserProfile>
а не UnitOfWork
и вместо этого передать этот макет в класс. Если это не так, то, скорее всего, у вашего тестируемого устройства слишком много обязанностей, и он нарушает SRP и LoD:)
Нет, _unitOfWork
становится переменной экземпляра. Так как ваш частный метод будет использовать эту переменную, он вызовет метод в экземпляре, который вы указали для конструктора. Это может быть ваша реализация базы данных, но также и ваш поддельный класс.