方法:
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
在私有方法中使用的事实,这是否意味着即使我已经模拟了对象并期望方法和返回值,它仍会命中数据库?如果确实如此,我该如何避免这种情况?我已经读过我应该将我的私有方法提取到一个单独的类中,但它已经使用工作单元进行了抽象。
答案 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
成为实例变量。因此,您的私有方法将使用该变量,它将在您提供给构造函数的实例上调用该方法。这可以是您的数据库实现,也可以是您的模拟类。