如果我在模拟对象上设置期望,它将在我正在测试的方法调用的私有方法中使用吗?

时间:2014-09-05 13:54:45

标签: c# unit-testing dependency-injection moq unit-of-work

方法:

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 个答案:

答案 0 :(得分:3)

解决您的问题

更改

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的lambda表达式在逻辑上可能与您在生产代码中使用的表达式相同,但它并不是指称对等的。这意味着正在调用您的GetSingle方法,但您在模拟中的lambda表达式 .Equals() ==到表达式在您的测试单元中调用,因此永远不会调用在Moq上设置的功能。由于默认情况下Moq存根方法,因此私有方法返回null,因为.GetSingle(Func<T,bool>)返回null,因为它被Moq存根。

为什么会发生这种情况

您可以通过在C#REPL中编写以下内容来查看此操作:

Func<bool> func1 = () => true;
Func<bool> func2 = () => true;
Console.WriteLine(func1 == func2);
// > False
Console.WriteLine(func1.Equals(func2));
// > False

我可以放心地假设您正确设置_unitOfWork,否则您将获得NullReferenceException,而不是您的方法因方法链而返回null。

代码设计

您可以通过在存储库中添加GetProfileByUsername(string)方法来解决此问题,而不是允许将lambda参数传递给方法。现在你的代码也非常糟糕,所以我建议这种方法。 UnitOfWork.Repository<UserProfile>().GetSingle(..)违反了得墨忒耳法则。此外,您的班级依赖于IRepository<UserProfile>,而依赖于您的UnitOfWork。所以,实际上,你应该嘲笑IRepository<UserProfile>而不是UnitOfWork并将 Mock传递给类。如果不是这种情况,那么您的受测单元可能有太多责任,并且违反了SRP 以及作为LoD:)

答案 1 :(得分:2)

不,_unitOfWork成为实例变量。因此,您的私有方法将使用该变量,它将在您提供给构造函数的实例上调用该方法。这可以是您的数据库实现,也可以是您的模拟类。