Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#7.0

Moq and out Parameter

0.00/5 (No votes)
18 Nov 2020CPOL2 min read 47.4K  
How to mock & test methods with out parameters
The solution uses features that are described in Moq’s Quickstart, and show how to mock and test methods with out parameters.

Introduction

Moq is a great mocking framework for .NET. It is used in unit testing to isolate the tested class from its dependencies and to make sure that the expected methods of the dependent objects are being called.

To carry out this task, those methods of the mocked objects which are expected to be called are set by various overloads of the Setup method. But to mock the method correctly could be not easy task when the method has ref/out parameters or is static. The post pays attention at how to mock the method with out parameter. The case of the static methods and static properties is described in the another post.

The solution uses features that are described in Moq’s Quickstart.

Background

Solution uses C#7, .NET 4.6.1, and NuGet packages Moq, FluentAssertions and xUnit.

Problem

Let’s consider the service that has the method with out parameter:

C#
public interface IService
{
	void ProcessValue(string inputValue, out string outputValue);
}

and the simple class that uses this service:

C#
public class Class1
{
	private readonly IService _service;

	public Class1(IService service)
	{
		_service = service;
	}

	/// <summary>
	/// Return the trimmed value of input string processed by service.
	/// </summary>
	/// <param name="inputValue">Input value, could be null.</param>
	/// <param name="outputValue">Output value.</param>
	public string ProcessValue(string inputValue)
	{
		_service.ProcessValue(inputValue, out string outputValue);
		return outputValue;
	}
}

Now we would like to write several tests that cover ProcessValue method.

Mock without callback

According to Moq’s Quickstart, out parameter could be mocked by the following code:

C#
// out arguments
var outString = "ack";
// TryParse will return true, and the out argument will return "ack", lazy evaluated
mock.Setup(foo => foo.TryParse("ping", out outString)).Returns(true);

This approach is used in the first test:

C#
[Theory]
[InlineData(null)]
[InlineData("short")]
[InlineData("\t\t\ttabbed\t\t")]
[InlineData("long long value")]
public void Class1_Return_ConstValueWithoutCallback(string inputValue)
{
	// arrange
	var expectedValue = "Expected value";
	var service = new Mock<IService>();
	service
		.Setup(mock => mock.ProcessValue(It.IsAny<string>(), out expectedValue))
		.Verifiable();

	// act
	var class1 = new Class1(service.Object);
	var actualValue = class1.ProcessValue(inputValue);

	// assert
	actualValue.Should().NotBeNull();
	actualValue.Should().Be(expectedValue);

	service.Verify();
}

In this case, out parameter has the predefined value that does not depend on input values. Moreover, if the tested routine expects that the value of the out parameter depends on input test parameters, this test could be modified in the following way:

C#
[Theory]
[InlineData(null)]
[InlineData("short")]
[InlineData("\t\t\ttabbed\t\t")]
[InlineData("long long value")]
public void Class1_Return_TheSameValueWithoutCallback(string inputValue)
{
	// arrange
	var expectedValue = $"Output {inputValue}";
	var service = new Mock<IService>();
	service
		.Setup(mock => mock.ProcessValue(It.IsAny<string>(), out expectedValue))
		.Verifiable();

	// the same code
	// ...
}

Sometimes it is useful, but if you’d like to get actual values of the passed arguments like inputValue or to set the value of the out parameter, Callback method should be used.

Mock with callback

According to Moq’s Quickstart, callbacks for methods with ref / out parameters are possible:

C#
// callbacks for methods with `ref` / `out` parameters are possible 
// but require some work (and Moq 4.8 or later):
delegate void SubmitCallback(ref Bar bar);

mock.Setup(foo => foo.Submit(ref It.Ref<Bar>.IsAny))
    .Callback(new SubmitCallback((ref Bar bar) => Console.WriteLine("Submitting a Bar!")));

So let’s define the delegate with the same signature as ProcessValue method from IService (using copy-paste approach):

C#
private delegate void ServiceProcessValue(string inputValue, out string outputValue);

Then IService mock could be defined with Callback method:

C#
[Theory]
[InlineData(null)]
[InlineData("short")]
[InlineData("\t\t\ttabbed\t\t")]
[InlineData("long long value")]
public void Class1_Return_NewValueWithCallback(string inputValue)
{
	// arrange
	string actualInputValue = null;

	const string outputValue = "Inner value";
	var expectedValue = "Not used value";
	var service = new Mock<IService>();
	service
		.Setup(mock => mock.ProcessValue(It.IsAny<string>(), out expectedValue))
		.Callback(new ServiceProcessValue(
			(string input, out string output) =>
			{
				actualInputValue = input;
				output = outputValue;
			}))
		.Verifiable();

	// act
	var class1 = new Class1(service.Object);
	var actualValue = class1.ProcessValue(inputValue);

	// assert
	actualValue.Should().NotBeNull();
	actualValue.Should().Be(outputValue);

	actualInputValue.Should().Be(inputValue);

	service.Verify();
}

Callback uses a delegate to create the return value and the passed values could be saved as is shown at the line 19. Moreover, at line 20, the value of the out parameter is set to outputValue and the value that is set in the Setup declaration is not used. It is asserted at line 32.

Solution

Provided solution has ConsoleApp console application and ConsoleApp.Tests test library.

ConsoleApp contains IService interface with an implementation class Service, and Class1 that consumes a service. In the main method, ProcessValue method is run over several values.

ConsoleApp.Tests test library contains considered above tests.


  1. All used IP-addresses, names of servers, workstations, domains, are fictional and are used exclusively as a demonstration only.
  2. Information is provided «AS IS».

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)