【问题标题】:Mock Unit Testing - Getting details of the classes implementing interface模拟单元测试 - 获取实现接口的类的详细信息
【发布时间】:2018-01-06 00:38:56
【问题描述】:

我有一个用户界面,我们称之为IUser。 有两种实现方式:AdminUser and NormalUser

现在,我正在尝试通过单元测试(模拟)来使用这些用户类。

我模拟界面如下:

var mockUser = new Mock<IUser>();

mockUser.get("username");

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

它永远不会在调试点停止,并且从 mockUser 实例中没有任何线索。

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

提前致谢。

【问题讨论】:

  • 两者都没有被调用。 Moq 使用接口创建自己的实现代理,并允许您配置行为。这似乎是XY problem。您要达到的最终目标是什么?
  • 您是在尝试测试实现还是依赖于它们的主题?
  • 您好 Nkosi,是的,我正在尝试测试接口的各个实现。任何线索我该怎么做?顺便说一句,谢谢你让我知道 XY 问题:)
  • 为了测试实际的实现,您需要初始化实际的实现,即new AdminUser()。如果其中任何一个实现具有外部依赖项,那么您将模拟那些(依赖项)并注入它们。如果一个类依赖于 IUser 并且您想测试该类,那么您将有理由模拟 IUser 以进行独立的单元测试。
  • 如果你想给单个类发文本,那么你不需要使用模拟和接口。只需创建类的直接实例并在其上调用方法。

标签: c# unit-testing mocking moq


【解决方案1】:

创建Mock&lt;IUser&gt; 实际上会创建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

如果我想测试ValidatePostalCodeIPostalCodeRegexProvider.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,或者在你的情况下是AdminUserNormalUser——这就是你要创建的实例。如果这些类依赖于其他接口,那么您可以为每个接口创建一个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 的设置可能会变得复杂且难以阅读和调试,但一个简单的类可以完成这项工作,甚至更好。

【讨论】:

    【解决方案2】:

    为了测试实际的实现,您需要初始化实际的实现,即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 以更好地了解如何使用起订量

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-29
      • 2020-09-28
      • 1970-01-01
      • 2023-03-04
      • 1970-01-01
      相关资源
      最近更新 更多