Setup expectation with successive function calls using Moq
In the Quickstart guide we find an example that shows us how to setup a different return value for each invocation as following:
// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.Execute("ping"))
.Returns(() => calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.Execute("ping"));
In Moq Triqs – Successive Expectations i found inspiration to implement an extension method that allows me to define an expectation that calls a set of successive functions:
public static class MoqExtensions
{
public static IReturnsResult<TMock> ReturnsInOrder<TMock, TResult>(this ISetup<TMock, TResult> setup, params Func<TResult>[] valueFunctions) where TMock : class
{
var functionQueue = new Queue<Func<TResult>>(valueFunctions);
return setup.Returns(() => functionQueue.Dequeue()());
}
}
This allows me to define a set of functions that i want to be called for each successive call:
public class WhenSettingUpOrderedExpectationFunctions
{
interface ICategory { int Id { get; } }
[Fact]
public void ShouldReturnTheSequenceOfIds()
{
var category = new Mock<ICategory>();
category.Setup(c => c.Id).ReturnsInOrder(
() => 1,
() => 2,
() => 3,
() => 4);
var expectedIds = new List<int> { 1, 2, 3, 4 };
foreach (var expectedId in expectedIds) Assert.Equal(expectedId, category.Object.Id);
}
}
That’s inspired, Tim!
Is there any reason why your “ReturnsInOrder” method couldn’t take an array of TResult rather than Func? Have you only done that for the added flexibility of lazy evaluation?
Thanks for the ping!
March 15th, 2009 at 22:34
One small thing: You could simply pass the valueFunctions array into the constructor of your Queue, rather than ‘foreaching’ over it and Enqueueing every item individually. Cuts one line out of the method and potentially speeds it up slightly.
March 15th, 2009 at 22:54
@Matt:
I ran into the problem of setting “ordered” results while playing with callbacks.. That is why i choose for functions instead of values.
You are absolutely right. While i was writing the method i looked for a constructed that accepted an IEnumerable but didn’t find see it. Now that i’m more awake, i could easily spot it
Thanks for the feedback.
March 16th, 2009 at 07:32
> I ran into the problem of setting “ordered” results while playing with callbacks.. That is why i choose for functions instead of values.
Fair enough. I took your code and “ported” it back to Moq 2.x, and while I was at it I added an overload which simply took an array of TResult, so it’s possible to have both! Pretty handy!
March 17th, 2009 at 01:20
Thank you for this code example. It is helping me mock an IDataReader with successive Read() calls.
July 29th, 2009 at 22:10
I found this worked, to replace the Funcs with values. Great starting point though, thanks for that; could not think for the life of me where to start with this problem.
public static class MoqExtensions
{
public static IReturnsResult ReturnsInOrder(this ISetup setup, params TResult[] values) where TMock : class
{
var valueQueue = new Queue(values);
return setup.Returns(valueQueue.Dequeue);
}
}
August 31st, 2009 at 20:54
Uggh. Forgot that HTML and Generics aren’t friends. Try this…
public static class MoqExtensions
{
public static IReturnsResult<TMock>ReturnsInOrder<TMock, TResult>(this ISetup<TMock, TResult setup, params TResult[] values) where TMock : class
{
var valueQueue = new Queue<TResult>(values);
return setup.Returns(valueQueue.Dequeue);
}
}
August 31st, 2009 at 20:57