98

I want a mock that returns 0 the first time, then returns 1 anytime the method is called thereafter. The problem is that if the method is called 4 times, I have to write:

mock.SetupSequence(x => x.GetNumber())
    .Returns(0)
    .Returns(1)
    .Returns(1)
    .Returns(1);

Otherwise, the method returns null.

Is there any way to write that, after the initial call, the method returns 1?

Connor Low
  • 5,900
  • 3
  • 31
  • 52
Florian
  • 4,507
  • 10
  • 53
  • 73

7 Answers7

79

The cleanest way is to create a Queue and pass .Dequeue method to Returns

.Returns(new Queue<int>(new[] { 0, 1, 1, 1 }).Dequeue);

Jakub Konecki
  • 45,581
  • 7
  • 87
  • 126
  • 3
    @RomainVerdier - No, it doesn't. I think the OP asks for a solution with 4 invocations. – Jakub Konecki Jul 03 '12 at 10:16
  • 12
    All - avoid my initial mistake. If you define it such that you have Returns(myQueue.Dequeue()) then you will only get the first result back - because you have actually dequeued the result, instead of supplying a lambda expression. – sfuqua Nov 15 '12 at 21:23
  • 2
    @sfuqua - that's why my answer uses a delegate instead of invocation. – Jakub Konecki Nov 16 '12 at 07:17
  • 8
    @JakubKonecki yes, exactly. I'm just warning others to follow that pattern precisely and do not accidentally Dequeue() as I did. – sfuqua Nov 19 '12 at 17:55
  • Small problem - Moq will not raise an exception if this setup was called less than 4 times. – Ben Dec 22 '14 at 22:01
  • 3
    @ben I like to put my exception throwing Moq statements (Verify's) in an Assert block (Arrange, Act, Assert) at the end of the test anyway, where you can cleanly check if something was called an exact number of times – Jacob McKay May 26 '16 at 22:44
  • Simple, yet efficient. Love it – Miiite May 16 '17 at 08:15
  • This should be the correct answer. Much more elegant. – Morten Sep 03 '20 at 07:46
  • 1
    I don't understand... this will be valid only 4 times, and afterwards not, isn't so? this is not what the OP asked for – Nissim Levy Jan 13 '21 at 01:05
  • 1
    I only get the first entry back, even if I use a delegate instead of a invocation. I'm using Moq 4, is that a problem? – Dominik Jul 21 '22 at 15:34
  • Same problem that @Dominik has, this does not seem to work in Moq 4 ... – dan-kli Mar 13 '23 at 14:23
64

That's not particulary fancy, but I think it would work:

    var firstTime = true;

    mock.Setup(x => x.GetNumber())
        .Returns(()=>
                        {
                            if(!firstTime)
                                return 1;

                            firstTime = false;
                            return 0;
                        });
Romain Verdier
  • 12,833
  • 7
  • 57
  • 77
  • 3
    I think this isn't thread-safe though, so in case your test involve code that invokes `GetNumber` from different threads - you might get `1` returned more than once. To achieve thread-safety, the `Interlocked` class can be used for read / write of `firstTime`. – yair Mar 19 '18 at 08:03
  • While it's pretty safe for the given scenario, you shouldn't get too comfortable using `if` statements in test logic. _Explanation_: https://www.typemock.com/avoid-logic-inside-a-unit-test/ – Connor Low May 28 '20 at 17:16
28

Bit late to the party, but if you want to still use Moq's API, you could call the Setup function in the action on the final Returns call:

var mock = new Mock<IFoo>();
mock.SetupSequence(m => m.GetNumber())
    .Returns(4)
    .Returns(() =>
    {
        // Subsequent Setup or SetupSequence calls "overwrite" their predecessors: 
        // you'll get 1 from here on out.
        mock.Setup(m => m.GetNumber()).Returns(1);
        return 1;
    });

var o = mock.Object;
Assert.Equal(4, o.GetNumber());
Assert.Equal(1, o.GetNumber());
Assert.Equal(1, o.GetNumber());
// etc...

I wanted to demonstrate using StepSequence, but for the OP's specific case, you could simplify and have everything in a Setup method:

mock.Setup(m => m.GetNumber())
    .Returns(() =>
    {
        mock.Setup(m => m.GetNumber()).Returns(1);
        return 4;
    });

Tested everything here with xunit@2.4.1 and Moq@4.14.1 - passes ✔

Connor Low
  • 5,900
  • 3
  • 31
  • 52
  • 2
    That doesn't seem to work with "ReturnsAsync". You can't have a lamda expression inside the method as a parameter. Maybe it works for non asynchronous development, but who does that nowadays? – Nick May 28 '20 at 09:32
  • 2
    The question wasn't about async development – Connor Low May 20 '21 at 14:41
8

Normally, I wouldn't bother submitting a new answer to such an old question, but in recent years ReturnsAsync has become very common, which makes potential answers more complicated.

As other have stated, you can essentially just create a queue of results and in your Returns call pass the queue.Dequeue delegate.

Eg.

var queue = new Queue<int>(new []{0,1,2,3});
mock.SetupSequence(m => m.Bar()).Returns(queue.Dequeue);

However, if you are setting up for an async method, we should normally call ReturnsAsync. queue.Dequeue when passed into ReturnsAsync and will result in the first call to the method being setup working correctly, but subsequent calls to throw a Null Reference Exception. You could as some of the other examples have done create your own extension method which returns a task, however this approach does not work with SetupSequence, and must use Returns instead of ReturnsAsync. Also, having to create an extension method to handle returning the results kind of defeats the purpose of using Moq in the first place. And in any case, any method which has a return type of Task where you have passed a delegate to Returns or ReturnsAsync will always fail on the second call when setting up via SetupSequence.

There are however two amusing alternatives to this approach that require only minimal additional code. The first option is to recognize that the Mock object's Setup and SetupAsync follow the Fluent Api design patterns. What this means, is that technically, Setup, SetupAsync, Returns and ReturnsAsync actually all return a "Builder" object. What I'm referring to as a Builder type object are fluent api style objects like QueryBuilder, StringBuilder, ModelBuilder and IServiceCollection/IServiceProvider. The practical upshot of this is that we can easily do this:

var queue = new List<int>(){0,1,2,3};
var setup = mock.SetupSequence(m => m.BarAsync());
foreach(var item in queue)
{
  setup.ReturnsAsync(item);
}

This approach allows us to use both SetupSequence and ReturnsAsync, which in my opinion follows the more intuitive design pattern.

The second approach is to realize that Returns is capable of accepting a delegate which returns a Task, and that Setup will always return the same thing. This means that if we were to either create an an extension method for Queue like this:

public static class EMs
{
  public static async Task<T> DequeueAsync<T>(this Queue<T> queue)
  {
    return queue.Dequeue();
  }
}

Then we could simply write:

var queue = new Queue<int>(new []{0,1,2,3});
mock.Setup(m => m.BarAsync()).Returns(queue.DequeueAsync);

Or would could make use of the AsyncQueue class from Microsoft.VisualStudio.Threading, which would allow us to do this:

var queue = new AsyncQueue<int>(new []{0,1,2,3});
mock.Setup(m => m.BarAsync()).Returns(queue.DequeueAsync);

The main problem that causes all of this, as that when the end of a setup sequence has been reached, the method is treated as not being setup. To avoid this, you are expected to also call a standard Setup if results should be returned after the end of the sequence has been reached.

I have put together a fairly comprehensive fiddle regarding this functionality with examples of the errors you may encounter when doing things wrong, as well as examples of several different ways you can do things correctly. https://dotnetfiddle.net/KbJlxb

Tikiman163
  • 101
  • 1
  • 2
  • 3
6

You can use a temporary variable to keep track of how many times the method was called.

Example:

public interface ITest
{ Int32 GetNumber(); }

static class Program
{
    static void Main()
    {
        var a = new Mock<ITest>();

        var f = 0;
        a.Setup(x => x.GetNumber()).Returns(() => f++ == 0 ? 0 : 1);

        Debug.Assert(a.Object.GetNumber() == 0);
        for (var i = 0; i<100; i++)
            Debug.Assert(a.Object.GetNumber() == 1);
    }
}
sloth
  • 99,095
  • 21
  • 171
  • 219
5

Just setup an extension method like:

public static T Denqueue<T>(this Queue<T> queue)
{
    var item = queue.Dequeue();
    queue.Enqueue(item);
    return item;
}

And then setup the return like:

var queue = new Queue<int>(new []{0, 1, 1, 1});
mock.Setup(m => m.GetNumber).Returns(queue.Denqueue);
Thomas Heijtink
  • 427
  • 5
  • 7
2

Moq uses the builder pattern, setting up the behavior of the mocks. You can, but don't have to use the fluent interface. Keeping that in mind, I solved the problem this way:

var sequence = myMock
            .SetupSequence(""the method I want to set up"");

foreach (var item in stuffToReturn)
{    
  sequence = sequence.Returns(item);
}
  • Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Jeremy Caney Nov 19 '22 at 00:24