Moq – Verifying parameters passed to a mocked method

When you need to verify that the code under test called a method with the expected parameters, you can mock the method with Moq and use Verify() + It.Is<T>() to check the parameters passed in. Verify() asserts that the method call happened as expected with the specified parameters.

Here’s an example. This is verifying that MessageService.Send(message) calls Repository.Save(json) when it’s a future message:

[TestMethod()]
public void TestSend_WhenFutureMessage_SavesMessageAsJsonForLater()
{
	//arrange
	var mockRepo = new Mock<IMessageRepository>();
	var messageService = new MessageService(mockRepo.Object);
	var futureMessage = new Message() { SendAt = DateTimeOffset.Now.AddDays(1) };

	//act
	messageService.Send(futureMessage);

	//assert
	mockRepo.Verify(t => t.Save(It.Is<string>(s => s.StartsWith("{"))));
}
Code language: C# (cs)

Note: It’s using a heuristic (the string starts with “{“) to determine if the string passed in is JSON or not.

In this article, I’ll show more examples of verifying parameters.

For more complex scenarios, you may want to capture the parameters and assert them directly by using the Callback() approach instead.

Verify primitive parameters

This section will show a few examples of verifying primitive (int, bool, string, etc…) parameters. The examples will be mocking the following repository interface:

public interface IMessageRepository
{
	public void Delete(bool cascading);
	public void Save(string json);
	public void InsertLog(int logId);
}
Code language: C# (cs)

Example – Hardcoded boolean value

When you’re matching an exact primitive value, you don’t need to use It.Is<T>(). You can simply hardcode the value:

//arrange
var mockRepo = new Mock<IMessageRepository>();
var messageService = new MessageService(mockRepo.Object);
var futureMessage = new Message() { SendAt = DateTimeOffset.Now.AddDays(1) };

//act
messageService.ProcessMessage(id: 1);

//assert
mockRepo.Verify(t => t.Delete(true));
Code language: C# (cs)

Example – String contains a substring

When you’re not matching exact values, you can use It.Is<T>() to examine the parameter value.

For example, let’s say you want to check if the JSON string contains the Message.Text value:

//arrange
var mockRepo = new Mock<IMessageRepository>();
var messageService = new MessageService(mockRepo.Object);
var futureMessage = new Message() 
{ 
	SendAt = DateTimeOffset.Now.AddDays(1),
	Text = "Hello World"
};

//act
messageService.Send(futureMessage);

//assert
mockRepo.Verify(t => t.Save(It.Is<string>(json => json.Contains("Hello World"))));
Code language: C# (cs)

Example – Int is between two values

There are specialized methods like It.IsInRange<T>() that can simplify assertions. For example, let’s say you’re checking if an int parameter is between a range of values:

//arrange
var mockRepo = new Mock<IMessageRepository>();
var messageService = new MessageService(mockRepo.Object);

//act
messageService.ProcessMessage(10);

//assert
mockRepo.Verify(t => t.InsertLog(It.IsInRange<int>(10, 20, Moq.Range.Inclusive)));
Code language: C# (cs)

Of course, you could always use It.Is<T>() to do the same thing:

mockRepo.Verify(t => t.InsertLog(It.Is<int>(t => t >= 10 && t <= 20)));
Code language: C# (cs)

Verify one parameter while accepting any values for other parameters

When the mocked method has multiple parameters, you may only be interested in examining some of the parameters. In that case, you can use It.IsAny<T>() to accept any values for the other parameters that you’re not interested in.

For example, let’s say you’re mocking out the following repository interface and you only want to examine the json parameter:

public interface IMessageRepository
{
	public void Save(int id, string json);
}
Code language: C# (cs)

Here’s how you’d do that:

//arrange
var mockRepo = new Mock<IMessageRepository>();
var messageService = new MessageService(mockRepo.Object);

//act
messageService.ProcessMessage(10);

//assert
mockRepo.Verify(t => t.Save(It.IsAny<int>(), It.Is<string>(s => s.StartsWith("{"))));
Code language: C# (cs)

This is accepting anything for the id parameter, and examining the json parameter.

Verify object parameters

This section will show examples of verifying object parameters. The examples will be mocking the following logger interface:

public interface ILogger
{
	public void Info(string message, object contextData);
	public void Error(Exception ex);
}
Code language: C# (cs)

Example – Object equality

When you pass an object to Verify(), it’ll first do a reference equality check.

For example, the following is verifying that the same Message reference is being passed into Logger.Info():

//arrange
var mockLogger = new Mock<ILogger>();
var messageService = new MessageService(mockLogger.Object);

var message = new Message()
{
	Text = "Let's code",
	SendAt = DateTimeOffset.Now.AddHours(1)
};

//act
messageService.Send(message);

//assert
mockLogger.Verify(t => t.Info("Sending message", message));
Code language: C# (cs)

If the references aren’t the same, then it’ll attempt to call Equals() (if it’s overridden).

For example, let’s say the code under test is creating a new Message object to pass to the logger:

Logger.Info("Sending message", new Message() { Text = message.Text, SendAt = message.SendAt });
Code language: C# (cs)

And Message.Equals() is overridden:

public class Message
{
	//rest of class

	public override bool Equals(object obj)
	{
		if (obj is Message msg)
		{
			return msg.Text == Text && msg.SendAt == SendAt;
		}
		else
		{
			return false;
		}
	}
}
Code language: C# (cs)

What happens when you pass the message object to Verify()?

mockLogger.Verify(t => t.Info("Sending message", originalMessage));
Code language: C# (cs)

The reference equality check fails, and it calls originalMessage.Equals(otherMessage), which returns true, making the Verify() assertion pass.

Example – Checking the object’s specific type

Let’s say you want to verify the specific exceptions that are being logged using Logger.Error(). You can use It.IsAny<T>() like this:

//arrange
var mockLogger = new Mock<ILogger>();
var messageService = new MessageService(mockLogger.Object);

var message = new Message() { Text = "Error time" };

//act
messageService.Send(message);

//assert
mockLogger.Verify(t => t.Error(It.IsAny<MessageSerializationException>()));
Code language: C# (cs)

Example – Verifying one of the object’s properties

You can use It.Is<T>() to verify one of the object’s properties. This can be useful for many reasons. For example, as a heuristic, you may want to only verify that the Message.Text property matches the expected value. Here’s how you’d do that:

//arrange
var mockLogger = new Mock<ILogger>();
var messageService = new MessageService(mockLogger.Object);

var message = new Message()
{
	Text = "Let's code",
	SendAt = DateTimeOffset.Now.AddHours(1)
};

//act
messageService.Send(message);

//assert
mockLogger.Verify(t => t.Info("Sending message", It.Is<Message>(m => m.Text == "Let's code")));
Code language: C# (cs)

4 thoughts on “Moq – Verifying parameters passed to a mocked method”

  1. What if the method takes a parameter that is a list and you want to verify that an item in the list has a certain property value?
    I tried this:
    _mockMyClass.Verify(x => x.MyMethod(It.Is<List<Something>>(list => list.Any(i => i.MyStringProperty == “some value”))));
    but it failed.

    Reply
    • Hi Adam,

      So that should definitely work. I just tried that scenario to confirm using the same class/method/List of Something and verifying a property on a Something object in the list. (Note: The comment system keeps stripping out the List’s type parameter, so I’m just saying ‘Something’).

      How the mock is configured? I’m assuming it’s:
      var _mockMyClass = new Mock<IMyClass>();

      Are you getting an error, or is the verification simply failing?

      One thing you can do to troubleshoot this is put a breakpoint in the test, right-click and pick Debug Test. Check the Mock object’s ‘invocations’ property. This shows the exact method calls/parameters. Double-check the invocations to see if it’s making the exact call you’re expecting with the exact same parameters.

      Reply

Leave a Comment