I would like to set up a method with Moq twice but it seems that the last one overrides the previous ones. Here's my initial setup:
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 });
This works out fine but I want this to return new ValidUserContext()
if the username or password is different to the username
and password
variables as above. To do that, I added another setup but this time it overrides the above one and always applies it:
membershipServiceMock.Setup(ms => ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>()) ).Returns( new ValidUserContext() );
What is the most elegant way of handling this type of situation with Moq?
Edit
I solved the problem with the below approach but I guess there is a better way of handling this:
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() );
Callbacks. A powerful capability of Moq is to attach custom code to configured methods and properties' getters and setters. This capability is often referred to as Callbacks.
Trying to mock a method that is called within another method. // code part public virtual bool hello(string name, int age) { string lastName = GetLastName(); } public virtual string GetLastName() { return "xxx"; } // unit test part Mock<Program> p = new Mock<Program>(); p. Setup(x => x. GetLastName()).
Verifies that all verifiable expectations have been met.
Moq supports this out of box with argument constraints:
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
also works, but the order is important:
// 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 });
If you look at the function definition for 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);
All you need to do is switch the order of the two Setup()
calls:
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 });
so if the input is indeed username
and password
, both Setup()
calls are qualified but the later one wins because of the rule and when you have any other inputs, only the first one is matched and applied.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With