模拟单元测试 - 获取实现接口的类的详细信息

时间:2018-01-06 00:38:57

标签: c# unit-testing mocking moq

我有一个用户界面,可以调用它IUser。 有两种实现方式:AdminUser and NormalUser

现在,我试图通过单元测试(Mocking)来使用这些用户类。

我按如下方式模拟界面:

var mockUser = new Mock<IUser>();

mockUser.get("username");

我在整个类中添加了breakkpoints,但我不确定调用哪个接口实例,即AdminUser or NormalUser

它永远不会停留在调试点,也不会从mockUser实例中找到线索。

如何获取mockUser模拟实例调用的类的详细信息?

提前致谢。

2 个答案:

答案 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,或者在您的情况下AdminUserNormalUser - 您将创建一个实例。如果这些类依赖于其他接口,那么您可以为每个接口创建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

相关问题