.NET 8 Moq mock GetRequiredKeyedService Setupreport an error
There are places where the code uses the
var serviceProvider = new Mock<IServiceProvider>();
(x => <AAA>(<BBB>())).Returns(new CCC());
I didn't realize it was an error.
Test method threw exception:
: Unsupported expression: x => (<Type>(), <object>())
Extension methods (here: ) may not be used in setup / verification expressions.
Stack Trace:
(MethodInfo method, Expression expression) line 87
(LambdaExpression expression, MethodInfo method, IReadOnlyList`1 arguments, Boolean exactGenericTypeArguments, Boolean skipMatcherInitialization, Boolean allowNonOverridable) line 236
ExpressionExtensions.<Split>g__Split|5_0(Expression e, Expression& r, MethodExpectation& p, Boolean assignment, Boolean allowNonOverridableLastProperty) line 256
(LambdaExpression expression, Boolean allowNonOverridableLastProperty) line 170
[TSetup](Mock mock, LambdaExpression expression, Func`4 setupLast, Boolean allowNonOverridableLastProperty) line 728
(Mock mock, LambdaExpression expression, Condition condition) line 562
Mock`[TResult](Expression`1 expression) line 645
It's kind of weird, isn't it?GetRequiredKeyedService
Not an interface method? Checking the .NET source code, sure enough, theGetRequiredKeyedService
is the IServiceProvider expansion method, and we know that Moq does not support the Setup expansion method.
/// <summary>
/// Get service of type <typeparamref name="T"/> from the <see cref="IServiceProvider"/>.
/// </summary>
/// <typeparam name="T">The type of service object to get.</typeparam>
/// <param name="provider">The <see cref="IServiceProvider"/> to retrieve the service object from.</param>
/// <param name="serviceKey">An object that specifies the key of service object to get.</param>
/// <returns>A service object of type <typeparamref name="T"/>.</returns>
/// <exception cref="">There is no service of type <typeparamref name="T"/>.</exception>
public static T GetRequiredKeyedService<T>(this IServiceProvider provider, object? serviceKey) where T : notnull
{
(provider);
return (T)(typeof(T), serviceKey);
}
It's half as good as finding the cause, looking through the source code and finding it step by step
First look at what method is called by (serviceType, serviceKey)
/// <summary>
/// IKeyedServiceProvider is a service provider that can be used to retrieve services using a key in addition
/// to a type.
/// </summary>
public interface IKeyedServiceProvider : IServiceProvider
{
/// <summary>
/// Gets the service object of the specified type.
/// </summary>
/// <param name="serviceType">An object that specifies the type of service object to get.</param>
/// <param name="serviceKey">An object that specifies the key of service object to get.</param>
/// <returns> A service object of type serviceType. -or- null if there is no service object of type serviceType.</returns>
object? GetKeyedService(Type serviceType, object? serviceKey);
/// <summary>
/// Gets service of type <paramref name="serviceType"/> from the <see cref="IServiceProvider"/> implementing
/// this interface.
/// </summary>
/// <param name="serviceType">An object that specifies the type of service object to get.</param>
/// <param name="serviceKey">The <see cref=""/> of the service.</param>
/// <returns>A service object of type <paramref name="serviceType"/>.
/// Throws an exception if the <see cref="IServiceProvider"/> cannot create the object.</returns>
object GetRequiredKeyedService(Type serviceType, object? serviceKey);
}
You can see that IKeyedServiceProvider is also inherited from the IServiceProvider interface, it's better to do, we directly Mock IKeyedServiceProvider and Setup can be used, will use the IServiceProvider place, change to the IKeyedServiceProvider, the code is as follows:
var serviceProvider = new Mock<IKeyedServiceProvider>();
(x => (<AAA>(), <BBB>())).Returns(new CCC());
Run the test, perfect.
summarize
It's not difficult to solve this problem, but it's still a bit of a headache if .Net isn't open source and you can't see the source code.