Если я устанавливаю ожидание для фиктивного объекта, будет ли он использоваться в частных методах, которые вызывает метод, который я тестирую?

Методы:

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 становится переменной экземпляра. Так как ваш частный метод будет использовать эту переменную, он вызовет метод в экземпляре, который вы указали для конструктора. Это может быть ваша реализация базы данных, но также и ваш поддельный класс.

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