C# – Mocking a method to return a different value when called a second time using Moq

I have been using Moq as my primary mocking framework in C# for several years now. Overall I really like it, the syntax is easy to read, the mock setup is straight forward, etc. However, there are a few cases I need to mock infrequently and I always forget how to do the setup. Often because I am cycling between Scala, Python, and C#. So in this post I am just listing those three cases in hopes they may help others, yet at the same time as a reference to myself!

You can download the example code from GitHub here.

Setup

The class SomeService represents a class our application will use to talk to some external service. In our tests we want to mock these service calls to test different scenarios. Here is the code we will be using in our mocking examples:

namespace MoqExample
{
    public interface ISomeService
    {
        SomeStuff GetNextStuff();
        void DoStuff();
    }
 
    public class SomeService : ISomeService
    {
        public SomeStuff GetNextStuff()
        {
            return new SomeStuff();
        }
 
        public void DoStuff()
        {
        }
    }
 
    public class SomeStuff
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

Mock Examples

Scenario – Return a different value the second time a mocked method is called

Let’s say I have a call in a service that returns the next item to process, such as pulling in customer requests from a queue that need to be processed in order, one at a time. In this case I might want to have a test that demonstrates the application can handle two different requests back to back, or even handle the case where there is an item to process, but the next item returned is empty, meaning there is nothing left to process. If I need to test this case there are several simple ways to do this with Moq.

Option 1 – Use a Queue

The more well known approach is to just use a Queue and have the mock call Dequeue and return the result each time the mocked method is called.

[Test]
public void Mock_ShouldReturnDifferentValue_WhenCalledASecondTimeUsingQueue()
{
    // Arrange
    Mock<ISomeService> _mockSomeService = new Mock<ISomeService>();

    var queueStuff = new Queue<SomeStuff>();
    queueStuff.Enqueue(new SomeStuff { Id = 1, Name = "Real" });
    queueStuff.Enqueue(null);

    _mockSomeService.Setup(x => x.GetNextStuff()).Returns(queueStuff.Dequeue);

    // Act
    var resultOne = _mockSomeService.Object.GetNextStuff();
    var resultTwo = _mockSomeService.Object.GetNextStuff();

    // Assert
    Assert.AreEqual("Real", resultOne.Name);
    Assert.IsNull(resultTwo);
}

Option 2 – Use a Sequence

The alternative is to use the Moq feature Sequence which allows you to set multiple return values, that will be returned one at a time in order, each time the mocked method is called.

[Test]
public void Mock_ShouldReturnsDifferentValue_WhenCalledASecondTimeUsingSequences()
{
    // Arrange
    Mock<ISomeService> _mockSomeService = new Mock<ISomeService>();

    _mockSomeService.SetupSequence(x => x.GetNextStuff())
            .Returns(new SomeStuff { Id = 1, Name = "Real" })
            .Returns((SomeStuff)null);

    // Act
    var resultOne = _mockSomeService.Object.GetNextStuff();
    var resultTwo = _mockSomeService.Object.GetNextStuff();

    // Assert
    Assert.AreEqual("Real", resultOne.Name);
    Assert.IsNull(resultTwo);
}

Scenario – Throw an exception the first time a mocked method is called, return a value the second time

In this scenario I want to test that my application will handle the case that when a call to a service throws an exception, it will retry and if it receives a valid response on the second try, process the request successfully and continue.

Option 1 – Use a Callback

You can accomplish this by using a Callback.

[Test]
public void Mock_ShouldSucceedOnSecondCall_WhenThrowsExceptionOnFirstCall()
{
    // Arrange
    Mock<ISomeService> _mockSomeService = new Mock<ISomeService>();
    var calls = 0;

    _mockSomeService.Setup(x => x.GetNextStuff())
        .Returns(() => new SomeStuff { Id = 1, Name = "Real" })
        .Callback(() =>
        {
            calls++;
            if (calls == 1)
                throw new Exception("Failure");
        });

    // Act
    var resultOneException = Record.Exception(() => _mockSomeService.Object.GetNextStuff());
    var resultTwo = _mockSomeService.Object.GetNextStuff();

    // Assert
    Assert.AreEqual("Failure", resultOneException.Message);
    Assert.AreEqual("Real", resultTwo.Name);
}

Option 2 – Use a Sequence

As an alternative to using a Callback, you can use the Moq feature Sequence for your mock setup.

[Test]
public void Mock_ShouldThrowExceptionOnSecondCall_WhenSucceedsOnFirstCallUsingSequences()
{
    // Arrange
    Mock<ISomeService> _mockSomeService = new Mock<ISomeService>();

    _mockSomeService.SetupSequence(x => x.GetNextStuff())
        .Throws(new Exception("Failure"))
        .Returns(new SomeStuff { Id = 1, Name = "Real" });

    // Act
    var resultOneException = Record.Exception(() => _mockSomeService.Object.GetNextStuff());
    var resultTwo = _mockSomeService.Object.GetNextStuff();
            

    // Assert
    Assert.AreEqual("Failure", resultOneException.Message);
    Assert.AreEqual("Real", resultTwo.Name);
}

Scenario – Mock a void method to throw an exception

Lets say I have some void method that normally just silently does some task for me and has no need to have a return type, such as a call to write a stat or a log entry. However, if I want to test how my application handles the case when this call throws an exception, I can use the following setup to mock this method.

[Test]
public void Mock_ShouldThrowException_WhenMockedVoidMethodIsCalled()
{
    // Arrange
    Mock<ISomeService> _mockSomeService = new Mock<ISomeService>();

    _mockSomeService.Setup(x => x.DoStuff())
        .Throws(new Exception("Failure"));

    // Act
    var resultException = Record.Exception(() => _mockSomeService.Object.DoStuff());

    // Assert
    Assert.AreEqual("Failure", resultException.Message);
}

I hope that helps!