Moq – How to return different values each time a mocked method is called

When you’re mocking a method that’s called multiple times, you may want to change the behavior of the method each time it’s called.

The way you do this with Moq is by using SetupSequence(), like this:

mock.SetupSequence(t => t.ShouldRetry())
	.Returns(true)
	.Returns(true)
	.Returns(false);

Note: You can also make it throw an exception in the sequence.

Example of code I want to test – it processes data in a loop

The DataProcessor class gets data from an external source in a loop. While there is data available, it processes it, converting the data to an ASCII string.

How would you unit test this?

public interface IDataSource
{
	bool HasData();
	byte[] GetNextDataBlock();
}

public class DataProcessor
{
	private readonly IDataSource DataSource;
	public DataProcessor(IDataSource dataSource)
	{
		DataSource = dataSource;
	}
	public string ProcessData()
	{
		StringBuilder sb = new StringBuilder();
		
		while(DataSource.HasData())
		{
			var data = DataSource.GetNextDataBlock();
			sb.Append(Encoding.ASCII.GetString(data));
		}

		return sb.ToString();
	}
}

Unit test that uses SetupSequence()

You want to prove that DataProcessor is getting all available data and returning it in the expected ASCII format.

To unit test this, you would mock out IDataSource and use SetupSequence() to mock the following behavior:

  • HasData() – returns true the first two times it’s called, then returns false.
  • GetNextDataBlock() – returns “hello ” the first time it’s called, then it returns “world”.
[TestMethod()]
public void TestProcessData_WhenHasTwoDataBlocks_ReturnsAsciiString()
{
	//arrange
	var mockDataSource = new Mock<IDataSource>();

	mockDataSource.SetupSequence(t => t.HasData())
		.Returns(true)
		.Returns(true)
		.Returns(false);

	mockDataSource.SetupSequence(t => t.GetNextDataBlock())
		.Returns(ASCIIEncoding.ASCII.GetBytes("hello "))
		.Returns(ASCIIEncoding.ASCII.GetBytes("world"));

	var dataProcessor = new DataProcessor(mockDataSource.Object);

	//act
	var data = dataProcessor.ProcessData();

	//assert
	Assert.AreEqual("hello world", data);
}

Leave a Comment