创建Mock<IUser> 实际上会创建IUser 的新 实现。因此,它不会帮助您测试任何实际实现。
使用Mock 的工作方式如下:
假设我有这个类和接口。该类验证邮政编码对于一个国家/地区是否有效。它依赖于为给定国家提供正则表达式模式的另一个接口。
public class PostalCodeValidator
{
private readonly IPostalCodeRegexProvider _regexProvider;
public PostalCodeValidator(IPostalCodeRegexProvider regexProvider)
{
_regexProvider = regexProvider;
}
public bool ValidatePostalCode(string postalCode, string countryCode)
{
var regex = _regexProvider.GetPostalCodeRegex(countryCode);
if (string.IsNullOrEmpty(regex)) return true;
return Regex.IsMatch(postalCode, regex);
}
}
public interface IPostalCodeRegexProvider
{
string GetPostalCodeRegex(string countryCode);
}
IPostalCodeRegexProvider 的实现可以是任何东西。它可以调用数据库,也可以是硬编码的。
但是当我为PostalCodeValidator 编写单元测试时,我明确不想要测试IPostalCodeRegexProvider 的真实实现。我想让IPostalCodeValidator 准确返回我想要的,这样我就可以确保PostalCodeValidator 有效。我只是在测试PostalCodeValidator。
如果我想测试ValidatePostalCode 在IPostalCodeRegexProvider.GetPostalCode 返回null 时返回true,那么我需要确保它将 返回null。这就是Mock 的用武之地。
它让我可以轻松地创建一个始终返回 null 的 IPostalCodeRegexProvider 的实现,因此我可以测试 ValidatePostalCode 对该 null 的作用。
[TestMethod]
public void ValidatePostalCode_ReturnsTrueWhenRegexIsNull()
{
var mock = new Mock<IPostalCodeRegexProvider>();
mock.Setup(x => x.GetPostalCodeRegex(It.IsAny<string>())).Returns(default(string));
var subject = new PostalCodeValidator(mock.Object);
Assert.IsTrue(subject.ValidatePostalCode("xyz","abc"));
}
无论测试的主题是什么——在这种情况下是PostalCodeValidator,或者在你的情况下是AdminUser 和NormalUser——这就是你要创建的实例。如果这些类依赖于其他接口,那么您可以为每个接口创建一个Mock。
您也可以考虑使用“测试替身”。您只需创建一个实现接口的简单类,而不是使用 Moq。例如,我对 Moq 所做的事情我也可以这样做:
public class PostalCodeRegexProviderThatReturnsNull : IPostalCodeRegexProvider
{
public string GetPostalCodeRegex(string countryCode)
{
return null;
}
}
现在单元测试看起来像这样:
public void ValidatePostalCode_ReturnsTrueWhenRegexIsNull()
{
var regexProvider = new PostalCodeRegexProviderThatReturnsNull();
var subject = new PostalCodeValidator(regexProvider);
Assert.IsTrue(subject.ValidatePostalCode("xyz","abc"));
}
这通常比使用Mock 更容易理解。有时,mock 的设置可能会变得复杂且难以阅读和调试,但一个简单的类可以完成这项工作,甚至更好。