Moq callback for ReturnsAsync - c#

I'm currently refactoring API to async actions and I need to refactor the tests for async. I have similar case as in the Moq documentation:
// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThing());
And I need to change it to:
// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThingAsync())
.ReturnsAsync(calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThingAsync());
But as the ReturnAsync() doesn't support lambda yet, the callback is called, but apparently in different context and thus the variable remains the value for the next call instead of being increased. Is there a way how to solve this?

In the first example you are passing a lambda to the Returns method, and that lambda is re-evaluated each time the mocked method is called. Because of this, you can actually combine Callback and Returns
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls++);
Console.WriteLine(mock.Object.GetCountThing());
In the second example you are passing the value 0 into the ReturnsAsync method, so that is why the mock returns zero each time. Although ReturnsAsync doesn't support passing a Func, you can still use Returns like so
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThingAsync())
.Returns(() => Task.FromResult(calls++));
Console.WriteLine(await mock.Object.GetCountThingAsync());

Related

Mock calling with return type delegate return test method delegate but not delegates from setup

There is a mock for a certain method, in it, depending on the input parameter, I determine which delegate will return. The problem is the following - when this method is called, the delegate set in returns is not called, but the delegate of the TEST method is returned.
How is it?
Mock setup
actionsProvider
.Setup(x => x.GetDelegateByCommandNameWithoutParams(It.IsAny<string>()))
.Returns((string x) =>
{
if (x == testCommand)
return Delegate.CreateDelegate(typeof(Func<int>),
typeof(ExecuteCodeLinesCommandTests).GetMethod(testMethod));
else if (x == testCommand2)
return Delegate.CreateDelegate(typeof(Func<int, int, int, int>),
typeof(ExecuteCodeLinesCommandTests).GetMethod(testMethod2));
else return null;
});
Mocking interface
public interface ICommandActionsProvider
{
public Delegate? GetDelegateByCommandNameWithoutParams(string commandName);
}
Method call
Delegate? del = commandActionProvider.GetDelegateByCommandNameWithoutParams(commandName);
Call return TEST method delegate, not from setup
I've already tried removing the delegate from returns, leaving just one return value. In this case, mock was called.
Instead of having branching inside the Returns try to create two Setups
actionsProvider
.Setup(x => x.GetDelegateByCommandNameWithoutParams(testCommand))
.Returns(Delegate.CreateDelegate(typeof(Func<int>),
typeof(ExecuteCodeLinesCommandTests).GetMethod(testMethod)));
actionsProvider
.Setup(x => x.GetDelegateByCommandNameWithoutParams(testCommand2))
.Returns(Delegate.CreateDelegate(typeof(Func<int, int, int, int>),
typeof(ExecuteCodeLinesCommandTests).GetMethod(testMethod2)));
If your default mock behaviour is loose then you will receive null for non-matching method calls. (If it is strict then it will throw exception.)

Moq setup treats all empty enumerables/arrays as the same parameter

I have a method that accepts an IEnumerable:
MyMethod(IEnumerable<MyClass> myParameter)
Now I am writing this code to mock the service:
var array1 = new MyClass[0];
var array2 = new MyClass[0];
_service
.Setup(s => s.MyMethod(array1))
.Returns(value1);
_service
.Setup(s => s.MyMethod(array2))
.Returns(value2);
And finally I am doing two calls to the service with both arrays inside system under test:
_service.MyMethod(array1);
_service.MyMethod(array2);
What I do expect is to get value1 and value2 from these calls, but in practice the latter call overrides the first one and I only get value2 from both calls.
Is this a bug in Moq or is this a feature that setup treats IEnumerable not as a separate object but rather tries to expand it and compare all elements or something (resulting in two empty arrays being the same setup call)?
When you create multiple setups on a method with Moq, each subsequent setup will replace the previous setup unless the setups are conditional (they specify certain conditions on the arguments). See this answer.
You can fix your code by specifying that the arguments must match the ones you intend to pass:
[Test]
public void MyTest()
{
var service = new Mock<MyClass>();
var array1 = new MyClass[0];
var array2 = new MyClass[0];
var value1 = "value1";
var value2 = "value2";
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array1))).Returns(value1);
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array2))).Returns(value2);
Assert.AreEqual(value1, service.Object.MyMethod(array1));
Assert.AreEqual(value2, service.Object.MyMethod(array2));
}
The behaviour you describe is the default behaviour of the moq, you can see it here. It indeed unfold enumerable and invoke IEnumerable.SequenceEqual. However that is default behaviour(if you setup using an instance, Constant matcher) and you could override it. The one approach is what Owen suggested to use It.Is<T> matcher, e.g.
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array1)))
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array2)))
Notice that == by default do ReferenceEquals() so this will make different non overridable setups.

Having trouble getting a Mocked method to return null

I'm mocking a method using Moq and I want the method to return null but it's not returning null and I'm not sure why.
This is my setup code:
var mock2 = new Mock<ReminderRepository>(stubPatientRemindersDBModelContainer);
mock2.CallBase = true;
mock2.Setup(x => x.GetPatientEscalations(userName, patientId, startDateTime, endDateTime, new DataTable()))
.Returns((PatientEscalationsDto)null);
When debugging, I was hoping that the variable assigned to GetPatientEscalations would be null but it isn't.
What am I doing wrong?
Check the arguments being passed into the mock setup.
mock2
.Setup(x => x.GetPatientEscalations(userName, patientId, startDateTime, endDateTime, new DataTable()))
.Returns((PatientEscalationsDto)null);
if they do not match what is actually passed in when invoking the member it will revert back to the base call as you have CallBase enabled.
Try loosening the expectation of the mocked member using the It.IsAny<T>() argument matchers
mock2
.Setup(x => x.GetPatientEscalations(
It.IsAny<string>(),
It.IsAny<int>(), //this is an assumption. use desired type here
It.IsAny<DateTime>(),
It.IsAny<DateTime>(),
It.IsAny<DataTable>()))
.Returns((PatientEscalationsDto)null);
That way any arguments passed will match and invoke the mocked members to behave as expected.

Mock Dependency Injection repository with filter

trying to mock repository :
var expMock = new Mock<IEntityRepository>();
expMock.Setup(s => s.GetMany(It.IsAny<Expression<Func<Entity, bool>>>()))
.Returns<IQueryable<Entity>>(r =>
new List<Entity>{ new Entity() } }.AsQueryable());
but when i call it:
IEnumerable<Entity> source = _entityRepository.GetMany(w => w.IsActive);
i get an exception:
System.ArgumentException : Object of type
'System.Linq.Expressions.Expression1[System.Func2[Entity,System.Boolean]]'
cannot be converted to type 'System.Linq.IQueryable`1[Entity]'.
Simply return value which you want your mocked method to return. In your case it will be IQueryable:
expMock.Setup(s => s.GetMany(It.IsAny<Expression<Func<Entity, bool>>>()))
.Returns(new List<Entity>{ new Entity() }.AsQueryable());
Generic parameter of Returns method is a type of the argument of invoked method. Returns<IQueryable<Entity>> means that GetMany method should be invoked with parameter of type IQueryable<Entity> which is not true of course. That's why you get this exception.
Method argument is the expression, so correct mock setup should look like:
.Returns<Expression<Func<Entity, bool>>>(e =>
new List<Entity> { new Entity() }.AsQueryable());
But thus you don't need method argument for providing returned result, use code above.
Your Returns() statement is binding your Func to be returned when GetMany() is called, not evaluating the expression and returning the result. It should work if you take out the r=>. You can probably get away without the type parameter as well.

Settings variable values in a Moq Callback() call

I think I may be a bit confused on the syntax of the Moq Callback methods. When I try to do something like this:
IFilter filter = new Filter();
List<IFoo> objects = new List<IFoo> { new Foo(), new Foo() };
IQueryable myFilteredFoos = null;
mockObject.Setup(m => m.GetByFilter(It.IsAny<IFilter>()))
.Callback( (IFilter filter) => myFilteredFoos = filter.FilterCollection(objects))
.Returns(myFilteredFoos.Cast<IFooBar>());
This throws a exception because myFilteredFoos is null during the Cast<IFooBar>() call. Is this not working as I expect? I would think FilterCollection would be called and then myFilteredFoos would be non-null and allow for the cast.
FilterCollection is not capable of returning a null which draws me to the conclusion it is not being called. Also, when I declare myFilteredFoos like this:
Queryable myFilteredFoos;
The Return call complains that myFilteredFoos may be used before it is initialized.
This is because the code in the Returns method is evaluated immediately; that is, when the Setup method is being invoked.
However, the callback isn't being invoked until the GetByFilter method is invoked.
Luckily, the Returns method is overloaded so that you can defer its execution as well:
mockObject.Setup(m => m.GetByFilter(It.IsAny<IFilter>()))
.Callback((IFilter filter) =>
myFilteredFoos = filter.FilterCollection(objects))
.Returns(() => myFilteredFoos.Cast<IFooBar>());
However, you don't need to save the value in a callback, because you can just get the parameter value directly in the Returns method:
mockObject.Setup(m => m.GetByFilter(It.IsAny<IFilter>()))
.Returns((IFilter filter) =>
filter.FilterCollection(objects).Cast<IFooBar>());
You can just take the parameter in the return value...
mockObject
.Setup(m => m.GetByFilter(It.IsAny<IFilter>()))
.Returns((IFilter filter) =>
{
myFilteredFoos = filter.FilterCollection(objects);
return myFilteredFoos.Cast<IFooBar>();
});

Categories