我有一个用户界面,可以调用它IUser
。
有两种实现方式:AdminUser and NormalUser
。
现在,我试图通过单元测试(Mocking)来使用这些用户类。
我按如下方式模拟界面:
var mockUser = new Mock<IUser>();
mockUser.get("username");
我在整个类中添加了breakkpoints,但我不确定调用哪个接口实例,即AdminUser or NormalUser
。
它永远不会停留在调试点,也不会从mockUser实例中找到线索。
如何获取mockUser模拟实例调用的类的详细信息?
提前致谢。
答案 0 :(得分:5)
创建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
返回null时测试true
返回IPostalCodeRegexProvider.GetPostalCode
,那么我需要确保它将返回null。这就是Mock
进来的地方。
它允许我轻松创建IPostalCodeRegexProvider
的实现,它始终返回null,因此我可以测试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
。
您还可以考虑使用&#34;测试双倍。&#34;您只需创建一个实现接口的简单类,而不是使用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
相比,这通常更容易理解。有时,模拟的设置会变得复杂,难以阅读和调试,但是一个简单的类可以完成这项工作,甚至更好。
答案 1 :(得分:1)
为了测试实际的实现,您需要初始化实际的实现,即new AdminUser()
。
例如
[TestMethod]
public void TestAdminUser {
//Arrange
IUser admin = new AdminUser();
//...set any necessary members relevant to the test
//Act
//...invoke member to be tested
//Assert
//...verify actual to expected behavior
}
如果其中一个实现具有外部依赖关系,那么您将模拟这些(依赖关系)并注入它们。
如果某个课程取决于IUser
public class SomeClass {
private readonly IUser user;
public SomeClass(IUser user) {
this.user = user;
}
//...
}
并且您想测试该类,然后您有理由模拟IUser
进行隔离单元测试。
[TestMethod]
public void TestSomeClass {
//Arrange
var username = "dummy";
var expected = "some value";
var mock = new Mock<IUser>();
//...set any necessary members relevant to the test
mock.Setup(_ => _.username).Returns(username);
var subject = new SomeClass(mock.Object);
//Act
//...invoke member to be tested
var actual = subject.SomeMethod();
//Assert
//...verify actual to expected behavior
Assert.AreEqual(actual, expected);
}
参考Moq Quickstart以更好地了解如何使用Moq