With Moq, is it valid to have more than one Matching Argument?
It.Is<string>()
In this example I want the mockMembershipService to return a different ProviderUserKey depending on the User supplied.
mockMembershipService.Setup(
x => x.GetUser(
It.Is<string>(
s => s.Contains("Joe")))
.ProviderUserKey)
.Returns("1234abcd");
mockMembershipService.Setup(
x => x.GetUser(
It.Is<string>(
s => s.Contains("Tracy")))
.ProviderUserKey)
.Returns("5678efgh");
The SetUp defaults to the second statement rather than evaluating each on its own merits.
Isn't it confusing? You are trying to mock GetUser method but you set the Returns for that function's return value's property. You also want to state return type's property based on mocked method.
Here's a way a more clear way:
mockMembershipService.Setup(x => x.GetUser(It.IsAny<string>())
.Returns<string>(GetMembershipUser);
Here's a method to create the membership mock:
private MembershipUser GetMembershipUser(string s)
{
Mock<MembershipUser> user =new Mock<MembershipUser>();
user.Setup(item => item.ProviderUserKey).Returns(GetProperty(s));
return user.Object;
}
Then you write a method for setting that property:
private string GetProperty(string s)
{
if(s.Contains("Joe"))
return "1234abcd";
else if(s.Contains("Tracy"))
return "5678efgh";
}
If you want to restrict input to just "Joe" and "Tracy", you can specify multiple conditions in It.Is<T>(). Something like
mockMembershipService.Setup(x => x.GetUser(It.Is<String>(s => s.Contains("Joe")
|| s.Contains("Tracy")))
.Returns<string>(/* Either Bartosz's or Ufuk's answer */);
Succesive Setup calls nullify previous setups.
You could use your argument in your return callback:
mockMembershipService.Setup(x => x.GetUser(It.IsAny<string>()).ProviderUserKey).Returns<string>(s =>
{
if(s.Contains("Joe"))
return "1234abcd";
else if(s.Contains("Tracy"))
return "5678efgh";
});
If it's important to you to assert the argument passed, you also need It.Is<string>(...) instead of It.IsAny<string>(...).
Please check Introduction to Moq > Matching Arguments documentation:
// matching Func<int>, lazy evaluated
mock.Setup(foo => foo.Add(It.Is<int>(i => i % 2 == 0))).Returns(true);
// matching ranges
mock.Setup(foo => foo.Add(It.IsInRange<int>(0, 10, Range.Inclusive))).Returns(true);
// matching regex
mock.Setup(x => x.DoSomething(It.IsRegex("[a-d]+", RegexOptions.IgnoreCase))).Returns("foo");
Related
I have the following (simplified) code:
public string methodName(ClassType object)
{
If(object.value == 1)
return "Yes";
else If(object.value == 2)
return "No";
else
return "whatever";
}
I am then calling this method in a unit test, and need to mock the return type based on the object value:
_Service.Setup(x => x.methodName(new methodName { value = 1}).Returns("Yes");
_Service.Setup(x => x.methodName(new methodName { value = 2}).Returns("No");
I know what I have written is wrong - but how can I achieve this?
You're on the right track. With Moq, you need to specify exactly which setup should match each input. You do it like this:
_Service.Setup(x => x.methodName(It.IsAny<ClassType>())).Returns("whatever");
_Service.Setup(x => x.methodName(It.Is<ClassType>(o => o.value == 1))).Returns("Yes");
_Service.Setup(x => x.methodName(It.Is<ClassType>(o => o.value == 2))).Returns("No");
The first line there sets up the mock to return "whatever" whenever this method is called with any value. The following two lines override that behavior for specific values.
I'd check out Moq's Quickstart guide for more details, and the Matching Arguments section in particular.
I´m not that familiar with Moq, however I asume the following should do it. You should provide a single Returns for every possible value for your parameter:
_Service.Setup(x => x.methodName(It.Is<ClassType>(y => y.value == 1))).Returns("Yes");
_Service.Setup(x => x.methodName(It.Is<ClassType>(y => y.value == 2))).Returns("No");
This way whenever your methodName-method is called with object.value == 1, "Yes" is returned, while object.value == 2 resolves to the method returning "No".
However to me this makes not much sense as you´re mocking the behaviour of methodName with the exact same behaviour. I suppose this is just for research.
There are many questions that have been already asked on this but I think I need something more basic that could clear this concept as I am beginner in TDD. I can't go forward till then.
Could you please go through following testmethod and explain if I have wrong understanding:
[Test]
public void ShouldSearch()
{
var ColumnList = new List<Column>();
The below line means that I am mocking object.
But what this It.IsAny<>() means here?
this.ColumnServiceMock.Setup(x => x.GetColumn(It.IsAny<Context>(), It.IsAny<Column>()))
.Returns(ColumnList);
var result = this.getColouminfo.GetFinalRecords(this.context, this.gridColumn);
this.ColumnServiceMock.Verify(x => x.GetColumn(It.Is<Context>(y => y == this.context),
It.Is<Column>(y => y.Id == 2)), Times.Once);
Assert.AreEqual(1, result.Data.Count, "Not equal");
Assert.IsTrue(result.Data.Success, "No success");
It.IsAny<T> is checking that the parameter is of type T, it can be any instance of type T. It's basically saying, I don't care what you pass in here as long as it is type of T.
this.ColumnServiceMock.Setup(x => x.GetColumn(It.IsAny<Context>(), It.IsAny<Column>())).Returns(ColumnList);
The above is saying whenever the GetColumn method is called with any parameters (as long as they are type of Context and Column respectively), return the ColumnList.
It.Is<T> allows you to inspect what was passed in and determine if the parameter that was passed in meets your needs.
this.ColumnServiceMock.Verify(x => x.GetColumn(It.Is<Context>(y => y == this.context), It.Is<Column>(y => y.Id == 2)), Times.Once);
The above is asserting that the GetColumn method was called exactly once with the Context parameter equal to this.Context and a Column parameter whose Id property equals 2.
Edit: Revisiting this answer years later with some more knowledge. this.ColumnServiceMock.Verify(x => x.GetColumn(It.Is<Context>(y => y == this.context), It.Is<Column>(y => y.Id == 2)), Times.Once); can be shortened to this.ColumnServiceMock.Verify(x => x.GetColumn(this.context, It.Is<Column>(y => y.Id == 2)), Times.Once);. You don't need to use It.Is to check for reference equality, you can just pass the object directly.
It.IsAny<T>() specifies anything thats of that type.
It.Is<T>() is more specific and takes a lamda to make sure it matches that exactly.
Both are just ways to specify an argument that you don't want to specify exactly when mocking. So for example if the argument is a string name and you don't care about the exact name in your test you can use:
It.IsAny<string>() in your mock specification, which will match any string.
If you always want the name to begin with "S" then you can do
It.Is<string>(x => x.StartsWith("S")) which will only match strings starting with S.
I currently have such a Moq expression
repo.Setup(r => r.GetProjectConfigurationById(It.Is<int>(s => s == response.Id)))
.Returns(response); // return response only if id matches setup one
As one can see, response is an object that has its own Id field.
Now I have a List<responses> and would like to transfer this expression into something that behaves as such:
get any integer Id
if Id is mathcing a response.Id, return that element of a list.
optionally, if match is not found, return null
How I could do that with Moq?
You can use the It.IsAny<int>() to match any parameter in GetProjectConfigurationById
There are also overloads of the Returns function where you can specify your custom condition using the parameter passed in to your GetProjectConfigurationById to look up the element by id or return null:
var responses = new List<Response>();
//...
repo.Setup(r => r.GetProjectConfigurationById(It.IsAny<int>()))
.Returns<int>(id => responses.SingleOrDefault(r => r.Id == id));
This code:
hub.MockedUserRepository.Setup(r => r.Update(It.IsAny<ControllUser>()))
.Callback((ControllUser usr) => Console.WriteLine("NULL = " + (usr.Zombies[0].ConnectionId == null)))
.Verifiable();
Will print
NULL = True
So i am thinking using this matching will catch it:
var zombieDisconnectParameterMatcher = It.Is<ControllUser>(x => x.Zombies[0].ConnectionId == null);
hub.MockedUserRepository.Setup(r => r.Update(zombieDisconnectParameterMatcher))
.Callback((ControllUser usr) => Console.WriteLine("NULL = " + (usr.Zombies[0].ConnectionId == null)))
.Verifiable();
But it does not.
Why?
By looking at the source code of It, it has to do with expression trees. I like the question; they can be quite puzzling. If you would take a look at the following method definitions:
public static TValue It.Is<TValue>(Expression<Func<TValue, bool>> match)
{
return Match<TValue>.Create(
value => match.Compile().Invoke(value),
() => It.Is<TValue>(match));
}
public static T Match.Create<T>(Predicate<T> condition, Expression<Func<T>> renderExpression)
{
// ...
return default(T);
}
If you would execute the following line:
var zombieDisconnectParameterMatcher = It.Is<ControllUser>(x => x.Zombies[0].ConnectionId == null);
Then It.Is<ControllUser>() will try to call a method called Match.Create<ControllUser>(), which returns the default of ControllUser. I assume ControllUser is a class and therefore zombieDisconnectParameterMatcher will be null. You should be able to see this with the debugger. So what actually you're calling is:
hub.MockedUserRepository.Setup(r => r.Update(null))
.Callback((ControllUser usr) => Console.WriteLine("NULL = " + (usr.Zombies[0].ConnectionId == null)))
.Verifiable();
When executing the Update method with a non-null ControllUser (from the method that is being tested for example), the callback will not trigger. It simply doesn't match the criteria since it's not null. You would see the verification fail, also.
To resolve this issue, either inline the zombieDisconnectParameterMatcher variable, or make it an expression typed variable (eg. Expression<Func<...>>). The latter will make sure that the code is not executed, but treated as an expression which the mock framework can reason about ('is Update being called with Zombies[0].ConnectionId == null?').
It depends how the ControllUser instance is instantiated. If the instance that you refer to within the mock is not the actual instance referred to in the code under test, the Setup will fail. You will need to ensure that the instance of ControllUser referred to in the code under test is the same object as the one in the test code. If it isn't, you'll have to test for it using It.IsAny<ControllUser>() and a callback, as your first example shows. It's hard to say with certainty without seeing more of the code that you're testing.
saveThemeAS.Verify(service => service.Execute(FakeUserID, It.Is<LayoutENT.Theme>(savedTheme =>
savedTheme != null
&& savedTheme.Name == FakeCopiedThemeName
&& savedTheme.ThemeID == 0
&& savedTheme.WidgetSkins.Any(skin => skin.Name == FakeWidgetSkinName && skin.WidgetSkinID == 0)
&& savedTheme.SiteStyles.Any(style => style.SiteStyleID == FakeStyleID && style.SiteStyleID == 0)
)));
Expected invocation on the mock at least once, but was never performed:
The basic problem here is I just get a failure, but I don't know specifically what failed/ what was not set appropriately as it could be any one of the 5 conditions above.
I would like to check each condition above seperately. Is there a slick way to do this with Moq without creating multiple tests? I thought it would be nice to be able to capture the argument passed to the mock and then test against that, but I haven't been able to find an elegant way to do so.
In essence, what you are asking for is for your inner lambda to perform a series of assertions rather than simply evaluate to a boolean value.
So let's do exactly that :)
saveThemeAS.Verify(service => service.Execute(FakeUserID, It.Is<LayoutENT.Theme>(savedTheme =>
{
Assert.IsNotNull(savedTheme);
Assert.AreEqual(FakeCopiedThemeName, savedTheme.Name);
Assert.AreEqual(0, savedTheme.ThemeID)
etc...
return true;
}
)));
You could move the assertions to a separate method for clearity.
If you want to get rid of the "return true;" you could write a wrapper for Action<T> that returns a Func<T,bool> that always returns true. That way you could write something along the lines of:
saveThemeAS.Verify(service => service.Execute(FakeUserID, It.Is<LayoutENT.Theme>(ReturnsTrue(CheckTheme))))
I think my approach to this problem would be to use a callback in the mock setup, such that the callback saves a copy of the savedTheme object that was passed to it. Then I'd perform asserts later in my unit test. This preserves the triple-A (Arrange, Act, Assert) pattern and seems cleaner than putting the assert logic into the mock setup. So for example:
protected static LayoutENT.Theme savedTheme;
// Arrange
themeParam = null;
saveThemeAS.Setup(service => service.Execute(FakeUserID, It.Is<LayoutENT.Theme>)
.Callback<layoutENT.Theme>(p => savedTheme = p);
// Act
// Calls the subject under test
// Assert
Assert.IsNotNull(savedTheme);
Assert.AreEqual(FakeCopiedThemeName, savedTheme.Name);
Assert.AreEqual(0, savedTheme.ThemeID)
etc...