tug*_*erk 43 .net c# unit-testing moq
我想用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
});
Run Code Online (Sandbox Code Playgroud)
这很好但我希望这返回new ValidUserContext()如果用户名或密码而不是上面的username和password变量.为此,我添加了另一个设置,但这次它会覆盖上面的设置并始终应用它:
membershipServiceMock.Setup(ms =>
ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>())
).Returns(
new ValidUserContext()
);
Run Code Online (Sandbox Code Playgroud)
使用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()
);
Run Code Online (Sandbox Code Playgroud)
k.m*_*k.m 53
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());
Run Code Online (Sandbox Code Playgroud)
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 });
Run Code Online (Sandbox Code Playgroud)
wat*_*HUN 10
如果您查看 的函数定义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);
Run Code Online (Sandbox Code Playgroud)
您需要做的就是切换两个调用的顺序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
});
Run Code Online (Sandbox Code Playgroud)
因此,如果输入确实是usernameand password,则两个Setup()调用都符合条件,但由于规则而后一个调用获胜,并且当您有任何其他输入时,仅匹配并应用第一个调用。
另一个开箱即用的选项是使用 Return<> 版本根据参数返回不同的 ValidUserContext。它并不比上面的答案更好,只是另一种选择。
我们设置 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;
}
}
Run Code Online (Sandbox Code Playgroud)