I mock a method from an interface which has optional parameters to return true
.
However, depending on whether I pass all the optional parameters to the call or only the required parameters I get a different result.
Here's a full working example of the behavior:
using Moq;
using System.Threading;
using System.Threading.Tasks;
using Xunit.Abstractions;
namespace MoqTest
{
public interface IProcedureService
{
Task<bool> InterfaceMethod(string req1, int req2, int? opt1 = null, CancellationToken opt2 = default);
}
public class Tests
{
Mock<IProcedureService> _procedureService = new Mock<IProcedureService>();
ITestOutputHelper _output;
public Tests(ITestOutputHelper output) => _output = output;
[Xunit.Fact]
public async void DoTheTest()
{
_procedureService.Setup(x => x.InterfaceMethod(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
_output.WriteLine($"result from IsUniqueProcedureNameInWorkspace (using optional defaults) " +
$"{(await _procedureService.Object.InterfaceMethod("", 0))}"); // returns false
_output.WriteLine($"result from IsUniqueProcedureNameInWorkspace (NOT using optional defaults) " +
$"{(await _procedureService.Object.InterfaceMethod("", 0, 0, default))}"); // returns true
}
}
}
This outputs the following:
Standard Output:
result from IsUniqueProcedureNameInWorkspace (using optional defaults) False
result from IsUniqueProcedureNameInWorkspace (NOT using optional defaults) True
I would expect them to both return True
as I set It.IsAny
for all the parameters in the Setup.
What am I missing here?
My Moq version is 4.20.70
It is indeed as Jon Skeet already suggested, but since I had the thought already while reading through the question (so before seeing his comment), and since I've also gone to the trouble of reproducing the issue, I take the liberty to write the following as an answer rather than a comment.
Instead of relying on output, which as to be inspected, I offer the solution as a test that uses assertions to demonstrate that the fix works:
[Fact]
public async Task FixWithAssertions()
{
_procedureService
.Setup(x => x.InterfaceMethod(
It.IsAny<string>(),
It.IsAny<int>(),
It.IsAny<int?>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
var result1 = await _procedureService.Object.InterfaceMethod("", 0);
var result2 = await _procedureService.Object.InterfaceMethod("", 0, 0, default);
Assert.True(result1);
Assert.True(result2);
}
This test passes (both assertions).
The only thing I chaged in Setup
was exactly that I've indicated It.IsAny<int?>()
rather than It.IsAny<int>()
. As Jon wrote, those aren't the same type, which may explain why the Setup
doesn't capture the call that omits the optional values.