Returning Sequences from Moq

No Comments August 8, 2013

I was asked earlier today about how to setup a mock object using Moq so that multiple calls to a method return different values. For example, given the following interface definition:

public interface IFoo {
    string GetText();
}

how would we set up a mock so that GetText() would return "foo", "bar" and "baz" in order?

The first naive attempt looked like this:

using FluentAssertions;
using Moq;
using NUnit.Framework;

namespace MoqDemo
{
    [TestFixture]
    public class MoqSequenceTests
    {
        [Test]
        public void MoqSequenceTest()
        {
            var fooMock = new Mock<IFoo>();

            int index = 0;
            var values = new[] { "foo", "bar", "baz" };
            fooMock.Setup(f => f.GetText())
                .Returns(() => values[index])
                .Callback(() => index++);

            IFoo foo = fooMock.Object;
            foo.GetText().Should().Be("foo");
            foo.GetText().Should().Be("bar");
            foo.GetText().Should().Be("baz");
        }
    }
}

Ok, it's hardly pretty but it does at least work. However, I don't like the idea of polluting the test with the values array and index and it’s not immediately obvious as to what the mock setup is actually doing. So how can we improve this? The first clue is in the signature of the Returns() method we’re using:

IReturnsResult<TMock> Returns(Func<TResult> valueFunction);

So if we can produce a function that returns the values we need in order then we can cut out the index and values array. With that in mind, we arrive at:

using System.Collections.Generic;
using FluentAssertions;
using Moq;
using NUnit.Framework;

namespace MoqDemo
{
    [TestFixture]
    public class MoqSequenceTests
    {
        [Test]
        public void MoqSequenceTest()
        {
            var fooMock = new Mock<IFoo>();

            fooMock.Setup(f => f.GetText())
                .Returns(new Queue<string>(new[] { "foo", "bar", "baz" }).Dequeue);

            IFoo foo = fooMock.Object;
            foo.GetText().Should().Be("foo");
            foo.GetText().Should().Be("bar");
            foo.GetText().Should().Be("baz");
        }
    }
}

The Dequeue method of Queue<string> returns a string (obviously), which means that we can use it as a method group. This means that every time the GetText method is invoked on our mock, Dequeue is called against the queue returning the next value.

As this is likely to be quite useful in a number of situations, it makes sense to turn this into an extension method:

using System.Collections.Generic;
using Moq.Language.Flow;

namespace MoqDemo
{
    public static class MoqExtensions
    {
        public static void Returns<T, TResult>(this ISetup<T, TResult> setup, params TResult[] results) where T : class
        {
            setup.Returns(new Queue<TResult>(results).Dequeue);
        }
    }
}

With this in place, our test is now reduced to:

using FluentAssertions;
using Moq;
using NUnit.Framework;

namespace MoqDemo
{
    [TestFixture]
    public class MoqSequenceTests
    {
        [Test]
        public void MoqSequenceTest()
        {
            var fooMock = new Mock<IFoo>();

            fooMock.Setup(f => f.GetText()).Returns("foo", "bar", "baz");

            IFoo foo = fooMock.Object;
            foo.GetText().Should().Be("foo");
            foo.GetText().Should().Be("bar");
            foo.GetText().Should().Be("baz");
        }
    }
}

Brilliant. Time to bundle up my extension method into a Nuget package and put it on my local server  and – oh, wait… What’s this SetupSequence method in Moq.SequenceExtensions?

using FluentAssertions;
using Moq;
using NUnit.Framework;

namespace MoqDemo
{
    [TestFixture]
    public class MoqSequenceTests
    {
        [Test]
        public void MoqSequenceTest()
        {
            var fooMock = new Mock<IFoo>();

            fooMock.SetupSequence(f => f.GetText())
                .Returns("foo")
                .Returns("bar")
                .Returns("baz");

            IFoo foo = fooMock.Object;
            foo.GetText().Should().Be("foo");
            foo.GetText().Should().Be("bar");
            foo.GetText().Should().Be("baz");
        }
    }
}

Bugger – all that work for nothing! Maybe next time I’ll have a closer look at the documentation…


No Comments