Moq - Is it possible to setup a mock without using It.IsAny

Viewed 229

I use Moq all the time for unit tests. Sometimes though I am mocking methods that have a lot of parameters.

Imagine a method like this:

public class WorkClient {

public void DoSomething(string itemName, 
   int itemCount, 
   ServiceClientCredential cred, 
   CancellationToken = default(CancellationToken){}
}

When I go to setup a mock, I end up having to do quite a lot of It.IsAny<T>(). I normally make one mocked instance per each test so I don't care about matching params.

But my mocks still look like this

var newMockClient = new Mock<WorkClient>();
newMockClient.Setup(x => x.DoSomething(
   It.IsAny<string>(), 
   It.IsAny<int>(),
   It.IsAny<ServiceClientCredential(),
   It.IsAny<CancellationToken>())
   .Returns(blah);

I would love to be able to just lazily instead use a LazySetup if it exists, like this.

newMockClient.Setup(x=>x.DoSomething()).Returns(blah);

Is there any lazy mode like this?

1 Answers

Based on this gist you can create an overload for SetupDefaultArgs which works well with void return type. You will need to add a reference to Moq.Language.Flow and System.Linq.Expressions;

public static ISetup<T> SetupDefaultArgs<T>(this Mock<T> mock, string methodName)
    where T : class
{
    var method = typeof(T).GetMethod(methodName);
    if (method == null)
        throw new ArgumentException($"No method named '{methodName}' exists on type '{typeof(T).Name}'");
            
    var instance = Expression.Parameter(typeof(T), "m");
    var callExp = Expression.Call(instance, method, method.GetParameters().Select(p => GenerateItIsAny(p.ParameterType)));
    var exp = Expression.Lambda<Action<T>>(callExp, instance);
    return mock.Setup(exp);
}

//helper method for above
private static MethodCallExpression GenerateItIsAny(Type T)
    {
        var ItIsAnyT = typeof(It)
            .GetMethod("IsAny")
            .MakeGenericMethod(T);
        return Expression.Call(ItIsAnyT);
    }

So, in your case the usage would look something like this:

public interface IWorkClient
{
    void DoSomething(string itemName, int itemCount,
        ServiceClientCredential cred,
        CancellationToken token = default(CancellationToken));
}
var mock = new Mock<IWorkClient>();
mock.SetupDefaultArgs(nameof(IWorkClient.DoSomething));

To make sure that it has been called you can do the following:

//Arrange
var mock = new Mock<IWorkClient>();
mock.SetupDefaultArgs(nameof(IWorkClient.DoSomething))
    .Callback(() => Console.WriteLine("DoSomething has been called"));

var cts = new CancellationTokenSource();

//Act       
mock.Object.DoSomething("1", 2, null, cts.Token);

//Assert
mock.Verify(client => client.DoSomething("1", 2, null, cts.Token), Times.Once);
Related