I want to quickly mock some method calls with Moq. I do have some long-winded method signatures with very long argument-type names and, lazy as I am, don't want to type out It.IsAny<>() for all of them when the method is setup to fail anyway, regardless of the amount of input-arguments.
More specifically, I came up with this extension-method, but T1 (the first argument) is not bound in the example call:
public static void SetResult<T,T1,TResult>(this Mock<T> mock, Func<T, Func<T1, TResult>> method, TResult result, Func<T1> filterT1 = null)
where T : class
{
if(filterT1 == null)
{
filterT1 = It.IsAny<T1>;
}
mock.Setup(m => method.Invoke(m).Invoke(filterT1.Invoke()))
.Returns(result);
}
// I want to use this like the following
Mock<Foo> mock = /* ... */;
mock.SetResult(foo => foo.bar, "Some return value"); // Doesn't work
mock.SetResult<Foo, int, string>(foo => foo.bar, "Some return value"); // Works
mock.SetResult(foo => foo.bar, "Some return value", () => 42); // Works too
Now my IDE, rightfully, complains that it has no idea what T1 is, because the type is nowhere explicitly used in the method-signature.
How can I change the SetResult-Method that I can still just reference the method on Foo in a simple, quick way, but without specifying all Type-Parameters?
Some more reliefs/constraints:
- You may use reflection to gather information about the method (for example its input-parameters).
- The call to
SetResultshould be as simple as possible, ideally only referencing the method to setup and the return-value. SetResultmust be expandable to any number of input-types (that is,barin the above example-call might also take aT2,T3, ...)- I'm aware that this requires multiple
SetResult-Definitions.
- I'm aware that this requires multiple
- Ideally I want to use optional arguments for the filters, which fall back from
nulltoIt.IsAny(as in the code-example). I could live without these filters and useIt.IsAnyfor all arguments.
Some tripwires specific to my code-sample (ignore for the general question):
Moq allows to mock function calls and inspect the input-parameters. These are some normal setups, which my above SetResult-Method is supposed to boil down to (the last one with IsAny).
// "Normal" setups
mock.Setup(m => m.bar(42)).Returns("Some value") // Only allow input 42
mock.Setup(m => m.bar(It.Is(i => i % 2 == 0))).Returns("Some value") // Only allow even inputs
mock.Setup(m => m.bar(It.IsAny<int>())).Returns("Some value") // Allow any int-input
Setup expects an Expression<Func<TObject,TResult>> (note the missing type-definitions for the bar-input-types) and inspects that to mock the call. Any It.*-calls can only be made in this expression (see this question and its comments of why I use *.invoke()).
The endresult will not only be methods that set results, but also exceptions, sequences, ...
But I can figure that out on my own.