Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set up a method twice for different parameters with Moq

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() ); 
like image 514
tugberk Avatar asked Oct 13 '12 09:10

tugberk


People also ask

What is callback in MOQ?

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.

How do you mock a method in C#?

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()).

What does MOQ verify do?

Verifies that all verifiable expectations have been met.


2 Answers

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 }); 
like image 159
k.m Avatar answered Sep 21 '22 13:09

k.m


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.

like image 37
watashiSHUN Avatar answered Sep 19 '22 13:09

watashiSHUN