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:
public interface IService
{
void ProcessValue(string inputValue, out string outputValue);
}
and the simple class that uses this service:
public class Class1
{
private readonly IService _service;
public Class1(IService service)
{
_service = service;
}
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:
var outString = "ack";
mock.Setup(foo => foo.TryParse("ping", out outString)).Returns(true);
This approach is used in the first test:
[Theory]
[InlineData(null)]
[InlineData("short")]
[InlineData("\t\t\ttabbed\t\t")]
[InlineData("long long value")]
public void Class1_Return_ConstValueWithoutCallback(string inputValue)
{
var expectedValue = "Expected value";
var service = new Mock<IService>();
service
.Setup(mock => mock.ProcessValue(It.IsAny<string>(), out expectedValue))
.Verifiable();
var class1 = new Class1(service.Object);
var actualValue = class1.ProcessValue(inputValue);
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:
[Theory]
[InlineData(null)]
[InlineData("short")]
[InlineData("\t\t\ttabbed\t\t")]
[InlineData("long long value")]
public void Class1_Return_TheSameValueWithoutCallback(string inputValue)
{
var expectedValue = $"Output {inputValue}";
var service = new Mock<IService>();
service
.Setup(mock => mock.ProcessValue(It.IsAny<string>(), out expectedValue))
.Verifiable();
}
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:
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):
private delegate void ServiceProcessValue(string inputValue, out string outputValue);
Then IService
mock could be defined with Callback
method:
[Theory]
[InlineData(null)]
[InlineData("short")]
[InlineData("\t\t\ttabbed\t\t")]
[InlineData("long long value")]
public void Class1_Return_NewValueWithCallback(string inputValue)
{
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();
var class1 = new Class1(service.Object);
var actualValue = class1.ProcessValue(inputValue);
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.
- All used IP-addresses, names of servers, workstations, domains, are fictional and are used exclusively as a demonstration only.
- Information is provided «AS IS».