【问题标题】:How to set up a method twice for different parameters with Moq如何使用 Moq 为不同的参数设置两次方法
【发布时间】:2012-10-04 00:38:03
【问题描述】:

我想使用 Moq 设置一个方法两次,但似乎最后一个覆盖了之前的。这是我的初始设置:

string username = "foo";
string password = "bar";

var principal = new GenericPrincipal(
    new GenericIdentity(username),
    new[] { "Admin" });

var membershipServiceMock = new Mock<IMembershipService>();
membershipServiceMock.Setup(ms =>
    ms.ValidateUser(username, password)
).Returns(new ValidUserContext { 
    Principal = principal
});

这很好,但如果用户名或密码与上面的 usernamepassword 变量不同,我希望它返回 new ValidUserContext()。为此,我添加了另一个设置,但这次它覆盖了上述设置并始终应用它:

membershipServiceMock.Setup(ms =>
    ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>())
).Returns(
    new ValidUserContext()
);

用 Moq 处理这种情况的最优雅的方法是什么?

编辑

我用下面的方法解决了这个问题,但我想有更好的方法来处理这个问题:

var membershipServiceMock = new Mock<IMembershipService>();
membershipServiceMock.Setup(ms =>
    ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>())
).Returns<string, string>((u, p) => 
    (u == username && p == password) ?
    new ValidUserContext { 
        Principal = principal
    }
    : new ValidUserContext()
);

【问题讨论】:

    标签: c# .net unit-testing moq


    【解决方案1】:

    Moq 通过参数约束支持这种开箱即用:

    mock.Setup(ms => ms.ValidateUser(
            It.Is<string>(u => u == username), It.Is<string>(p => p == password))
        .Returns(new ValidUserContext { Principal = principal });
    mock.Setup(ms => ms.ValidateUser(
            It.Is<string>(u => u != username), It.Is<string>(p => p != password))
        .Returns(new ValidUserContext());
    

    Catch-all It.IsAny 也可以,但顺序很重要:

    // general constraint first so that it doesn't overwrite more specific ones
    mock.Setup(ms => ms.ValidateUser(
            It.IsAny<string>(), It.IsAny<string>())
        .Returns(new ValidUserContext());
    mock.Setup(ms => ms.ValidateUser(
            It.Is<string>(u => u == username), It.Is<string>(p => p == password))
        .Returns(new ValidUserContext { Principal = principal });
    

    【讨论】:

    • 第一个解决方案不处理 u == 用户名和 p != 密码的情况,还是这样?
    【解决方案2】:

    如果您查看Setup() 的函数定义:

    // Remarks:
    //     If more than one setup is specified for the same method or property, the latest
    //     one wins and is the one that will be executed.
    public ISetup<T, TResult> Setup<TResult>(Expression<Func<T, TResult>> expression);
    

    您需要做的就是切换两个Setup() 调用的顺序

    membershipServiceMock.Setup(ms =>
        ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>())
    ).Returns(
        new ValidUserContext()
    );
    membershipServiceMock.Setup(ms =>
        ms.ValidateUser(username, password)
    ).Returns(new ValidUserContext { 
        Principal = principal
    });
    

    因此,如果输入确实是 usernamepassword,则 Setup() 调用都是合格的,但由于规则,后一个会获胜,并且当您有任何其他输入时,只有第一个会匹配并应用。

    【讨论】:

      【解决方案3】:

      另一个开箱即用的选项是使用 Return 版本根据参数返回不同的 ValidUserContexts。这并不比上面的答案更好,只是另一种选择。

      我们设置 ValidateUser() 以返回函数 GetUserContext(string, string) 的结果,传入调用 ValidateUser() 时使用的用户名和密码。

      [TestClass]
      public class MultipleReturnValues {
      
          public class ValidUserContext {
              public string Principal { get; set; }
          }
      
          public interface IMembershipService {
              ValidUserContext ValidateUser(string name, string password);
          }
      
          [TestMethod]
          public void DifferentPricipals() {
      
              var mock = new Mock<IMembershipService>();
              mock.Setup(mk => mk.ValidateUser(It.IsAny<string>(), It.IsAny<string>())).Returns<string, string>(GetUserContext);
      
              var validUserContext = mock.Object.ValidateUser("abc", "cde");
      
              Assert.IsNull(validUserContext.Principal);
      
      
              validUserContext = mock.Object.ValidateUser("foo", "bar");
      
              Assert.AreEqual(sPrincipal, validUserContext.Principal);
      
      
          }
      
          private static string sPrincipal = "A Principal";
          private static ValidUserContext GetUserContext(string name, string password) {
      
              var ret = new ValidUserContext();
      
              if (name == "foo" && password == "bar") {
                  ret = new ValidUserContext { Principal = sPrincipal };
              }
              return ret;
      
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-02
        • 1970-01-01
        • 2011-05-04
        • 2016-03-15
        • 2012-07-01
        相关资源
        最近更新 更多