【问题标题】:Moq - how to mock the result of a method within a method begin testedMoq - 如何在方法开始测试中模拟方法的结果
【发布时间】:2012-02-12 07:37:43
【问题描述】:

并提前感谢您的任何和所有帮助。

我有一个正在尝试测试的方法。

在这个方法中调用了 UserMembership.Validate() //自定义覆盖,但代码还不能正常工作,超出了测试范围。

因此我想模拟(使用 moq)返回结果,以便方法的实际测试能够成功。

这里是代码

public LoginResponse Login(LoginRequest request)
{
    var response = new LoginResponse(request.RequestId);

    // Validate client tag and access token
    if (!ValidateRequest(request, response, Validate.ClientTag | Validate.AccessToken))
        return response;

    if (!UserMembership.ValidateUser(request.UserName, request.Password))
    {
        response.Acknowledge = AcknowledgeType.Failure;
        response.Messages = "Invalid username and/or password.";
        //response.MessageCode = -4;
        return response;
    }

    _userName = request.UserName;

    return response;
}

所以,我的测试是针对 LoginResponse() 但我想将 UserMembership 返回值(布尔值)“伪造”为 true...

我相信你们很简单。

TIA,休。

【问题讨论】:

    标签: unit-testing methods moq


    【解决方案1】:

    您可能可以将您的问题重新命名为“您如何在 99% 的情况下使用带有单元测试的模拟框架”,因为您正朝着正确的方向前进——这是一种非常典型的用法。

    您将要从 UserMembership 类中提取一个接口(在该类中右键单击,选择“重构”,然后选择“提取接口。”),然后使用 Moq 创建该接口的模拟实例以供在其中使用你的测试。然后,您可以使用 Moq 来“设置”该模拟的行为,以便在测试期间执行您希望它执行的任何操作。语法如下所示:

    var userMembershipMock = new Mock<IUserMembership>();
    userMembershipMock.Setup(m=> m.ValidateUser(It.Is<string>(str=> str == "myUserName"), It.Is<string>(str=> str == "myPassword"))).Returns(true);
    

    然后你将创建你的类的一个新实例,传入你的 IUserMembership 模拟实例(但由于你会让你的类的构造函数接受接口类型的参数,你的类不会关心你是否通过它是一个模拟或实际的 UserMembership 实例

     MyClass myClass = new MyClass(userMembershipMock.Object);
    

    之后您可以开始实际测试 MyClass 的行为:

    var request = new LoginRequest { UserName = "myUserName", Password = "myPassword" };
    LoginResponse response = myClass.Login(request);
    

    然后你可以断言你的班级的反应是你所期望的:

    Assert.AreEqual(AcknowledgeType.Success, response.Acknowledge);
    

    或者您可以验证您的模拟方法(或属性)是否已按预期调用:

    userMembershipMock.Verify(m=> m.ValidateUser(It.Is<string>(str=> str == "myUserName"), It.Is<string>(str=> str == "myPassword")), Times.Once());
    

    等等。

    Moq quick start page 有点像一页纸,可以教你 99% 的使用知识。

    【讨论】:

      【解决方案2】:

      在这种情况下,我能想到模拟 UserMembership 的唯一方法(假设它不是属性)是使用 IoC 框架,如 Castle WindsorNinject。当您使用 IoC 容器时,您将对 UserMembership 的调用重构为接口 (IUserMembership) 并使用容器提供实现:

      if (Container.Resolve<IUserMembership>().ValidateUser(request.UserName, request.Password))
      

      然后在您的单元测试设置中,您将 IUserMembership 的实现注册为模拟对象:

      var mock = new Mock<IUserMembership>();
      Container.Register<IUserMemberhip>().Instance(mock.Object);
      

      您还必须创建一个生产实现。如果这是标准的 UserMembership 类,那么这个实现可能除了 UserMembership 什么都不做。不过,还有其他方法可以模仿这种鸭子类型。

      【讨论】:

      • 您好凯洛蒂,感谢您的回复。这也是我所想的,也是我在示例中使用自定义成员资格的原因,因为它并不是那些易于为 Castle 提供易于伪造的接口的类之一。我想我想要了解的是如何在没有数据库请求的情况下测试成员资格,并尝试了解更多关于 Mocking 的信息……除了更通用的测试功能外,我没有跟上速度。我期待看到是否有其他关于 Mocks 的回复。再次感谢你,休
      • 是的,这是一个有点复杂的话题,我没有什么特别好的文章给你。在模拟 .NET 框架类时,我们经常需要这种模式。我认为你的绊脚石是这需要一些多余的工作 - 你必须创建一个愚蠢的类,其唯一目的是调用 UserMembership 的方法。幸运的是,有人 (claassen.net/geek/blog/tag/castle) 已经想出了一个自动化的解决方案来简化这件事。
      • 我已经同意stackoverflow.com/questions/1465849/using-ioc-for-unit-testing 的帖子,即最好让您的 IoC 配置远离您的单元测试,这样您就可以专注于仅为那些直接需要的依赖项创建模拟您正在测试的代码,这样做可以避免将单元测试与 IoC 框架耦合,并且还避免了为了运行单个测试而必须配置一些不需要的嵌套依赖项的深层层次结构的可能性。
      • @ardave - 在我写下这个答案的那一年,我完全同意你的看法。由于过度耦合的层,将 IoC 配置与单元测试混合会导致比它应该具有的好处更多的痛苦。如果我应该删除我的答案,请支持此评论。
      • 艰难的决定,凯洛蒂。由你决定。我一遇到这个特定的决策点(测试中的 IoC)就认出了它,但在那之后的一段时间,我才能够认出选择一种或另一种方式的任何原因。
      猜你喜欢
      • 2021-04-02
      • 2014-02-23
      • 1970-01-01
      • 2014-01-18
      • 1970-01-01
      • 2022-09-28
      • 1970-01-01
      • 2011-05-21
      • 2017-10-14
      相关资源
      最近更新 更多