如何使用 Moq 为不同的参数设置两次方法
Posted
技术标签:
【中文标题】如何使用 Moq 为不同的参数设置两次方法【英文标题】:How to set up a method twice for different parameters with 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
);
这很好,但如果用户名或密码与上面的 username
和 password
变量不同,我希望它返回 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()
);
【问题讨论】:
【参考方案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
);
因此,如果输入确实是 username
和 password
,则 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;
【讨论】:
以上是关于如何使用 Moq 为不同的参数设置两次方法的主要内容,如果未能解决你的问题,请参考以下文章
如何在没有明确指定或使用重载的情况下 Moq 在其签名中具有可选参数的方法?