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
SetResult
should be as simple as possible, ideally only referencing the method to setup and the return-value. SetResult
must be expandable to any number of input-types (that is,bar
in 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
null
toIt.IsAny
(as in the code-example). I could live without these filters and useIt.IsAny
for 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.