.NET 8 Moq mock GetRequiredKeyedService Setup报错

代码有地方用到了IServiceProvider.GetRequiredKeyedService来解析服务,写单元测试时也需要Mock它,本以为像下面这些些就可以

var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(x => x.GetRequiredKeyedService<AAA>(It.IsAny<BBB>())).Returns(new CCC());

没想到报错了:

  Test method threw exception:
System.NotSupportedException: Unsupported expression: x => x.GetRequiredKeyedService(It.IsAny<Type>(), It.IsAny<object>())
Extension methods (here: ServiceProviderKeyedServiceExtensions.GetRequiredKeyedService) may not be used in setup / verification expressions. Stack Trace: 
Guard.IsOverridable(MethodInfo method, Expression expression) line 87
MethodExpectation.ctor(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
ExpressionExtensions.Split(LambdaExpression expression, Boolean allowNonOverridableLastProperty) line 170
Mock.SetupRecursive[TSetup](Mock mock, LambdaExpression expression, Func`4 setupLast, Boolean allowNonOverridableLastProperty) line 728
Mock.Setup(Mock mock, LambdaExpression expression, Condition condition) line 562
Mock`1.Setup[TResult](Expression`1 expression) line 645

有点奇怪,难道GetRequiredKeyedService不是接口方法?查看.NET源代码,果然,GetRequiredKeyedService是IServiceProvider的扩招方法,我们知道Moq是不支持Setup扩招方法的。

/// <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="System.InvalidOperationException">There is no service of type <typeparamref name="T"/>.</exception>
public static T GetRequiredKeyedService<T>(this IServiceProvider provider, object? serviceKey) where T : notnull
{
ThrowHelper.ThrowIfNull(provider); return (T)provider.GetRequiredKeyedService(typeof(T), serviceKey);
}

原因找到就好半了,翻看源码,一步步找到IServiceProvider.GetRequiredKeyedService最终调用的接口方法然后再mocke即可

首先看下requiredServiceSupportingProvider.GetRequiredKeyedService(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="ServiceDescriptor.ServiceKey"/> 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);
}

可以看到IKeyedServiceProvider也是继承了IServiceProvider接口,这就更好办了,我们直接Mock IKeyedServiceProvider并Setup即可,将用到IServiceProvider的地方,换成IKeyedServiceProvider,代码如下:

var serviceProvider = new Mock<IKeyedServiceProvider>();
serviceProvider.Setup(x => x.GetRequiredKeyedService(It.IsAny<AAA>(), It.IsAny<BBB>())).Returns(new CCC());

运行测试,完美。

总结

解决这个问题并不困难,但是如果.Net不开源,看不到源代码,还是有点头疼。

.NET 8 Moq mock GetRequiredKeyedService Setup报错的更多相关文章

  1. cap deploy:setup报错

    今天部署cap的时候,setup出现以下错误: 查询半天未果,不过最后还是在google找到了,可见度娘极为不靠谱! I had the same error on deploy:setup with ...

  2. Mac Mojave(10.14.1)执行Matlab的mex报错

    先装了matlab2018b,发现很频繁的crash,同时考虑到要跑的代码在>=2017a时就计算错误,于是转战matlab2016b matlab2016b安装后,执行mex -setup报错 ...

  3. Selenium Grid 运行报错 Exception thrown in Navigator.Start first time ->Error forwarding the new session Empty pool of VM for setup Capabilities

    Selenium Grid 运行报错 : Exception thrown in Navigator.Start first time ->Error forwarding the new se ...

  4. python setup.py install 报错ImportError: No module named setuptools

    学习光荣之路python课程时,使用python setup.py install安装其他模块时,第一次安装某模块成功了.安装另一模块却报错ImportError: No module named s ...

  5. pip安装mysql-python报错:Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-enRreC/mysql-python/

    公司业务开发,用python开发网站;需要使用模块MySQLdb. 我直接pip install MySQLdb,当然不成功了,模块名字因该是mysql-python pip install mysq ...

  6. python pip install 报错TypeError: unsupported operand type(s) for -=: 'Retry' and 'int' Command "python setup.py egg_info" failed with error code 1 in

    pip install http://download.pytorch.org/whl/cu80/torch-0.2.0.post3-cp27-cp27mu-manylinux1_x86_64.whl ...

  7. mock测试SpringMVC controller报错

    使用mock测试Controller时报错如下 java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig at org.spr ...

  8. python setup.py install 报错:error: [WinError 3] 系统找不到指定的路径。: 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\PlatformSDK\\lib

    Outline 在通过 setup.py 安装python模块时,遇到了以下报错: # 执行 python setup.py install # 报错: error: [WinError 3] 系统找 ...

  9. python setup.py install 报错

    python setup.py install 报错信息 [root@VM_25_28_centos psutil-2.0.0]# python setup.py install running in ...

  10. python下pip 安装 pyautogui报错Command "python setup.py egg_info" failed with error code 1 in C:\Users\Administrator\AppData\Local\Temp\pip-install-svhtepho\pygetwindow\

    python装的3.6 64位,使用命令pip install pyautogui 或者pip install -U pyautogui 都失败了 报错如下: Command "python ...

随机推荐

  1. Asp .Net Core 系列:基于 Castle DynamicProxy + Autofac 实践 AOP 以及实现事务、用户填充功能

    目录 什么是 AOP ? .Net Core 中 有哪些 AOP 框架? 基于 Castle DynamicProxy 实现 AOP IOC中使用 Castle DynamicProxy 实现事务管理 ...

  2. SpringBoot中的一些组件

    Redis 引入data-redis-starter <dependency>    <groupId>org.springframework.boot</groupId ...

  3. 2. 标签上 title 与 alt 属性的区别是什么?

    1. alt 是给搜索引擎识别,在图像无法显示时的替代文本2. title 是关于元素的注释信息,主要是给用户解读.3.当鼠标放到文字或是图片上时有 title 文字显示.(因为 IE 不标准)在 I ...

  4. react懒加载,减少首屏加载时间

    最近在写一个react-ant-admin的集成框架用于快速搭载中后台项目.其中遇到很多问题,最重要的应该是访问速度了.我就想 react 可不可以和 vue 一样用路由懒加载来减少首页渲染所花费的时 ...

  5. 推荐一枚宝藏Up主,顺便聊聊感想

    众所周知,B站是学习网站 最近发现一宝藏Up主,主要做科普,主题包括但不限于:大模型的底层算法.量子计算底层原理和硬件设计,以及其他物理或者自然科学主题,总体偏向于理工科. 值得推荐的理由:Up主对底 ...

  6. vue小知识~使用$attrs和$listeners接收父组件转来的属性和方法

    一般下我们子组件获取父组件传过来的的值得时候,我们是使用props来接收,但是如果我们父组件转过来的值是给孙组件使用时,使用哪个props接收的方式太过麻烦. 这是vue为我们提供了一个实例$attr ...

  7. CF30D King's Problem? 题解

    CF30D 题意 有 \(n+1\) 个点,其中的 \(n\) 个点在数轴上.求以点 \(k\) 为起点走过所有点的最短距离,允许重复. 思路 有两种情况: \(k\) 在数轴上(如图1). \(k\ ...

  8. OpenAI深夜丢炸弹硬杠谷歌搜索

    这几年科技变革太快,AI更是飞速发展,作为一名IT老兵,使用过的搜索引擎也是一换再换.这不,刚消停了一段时间的OpenAI又丢出一个炸弹SearchGPT,直接跟谷歌掀桌子了. 1.谷歌搜索的无奈 早 ...

  9. FFmpeg开发笔记(四十一)结合OBS与MediaMTX实现SRT直播推流

    ​<FFmpeg开发实战:从零基础到短视频上线>一书的"10.2  FFmpeg推流和拉流"提到直播行业存在RTSP和RTMP两种常见的流媒体协议.除此以外,还有于20 ...

  10. 【MySQL】Tinyint 类型问题

    下发字段: `DISTRIBUTION_STATUS` tinyint(1) DEFAULT '0' COMMENT '下发状态,0未下发,1已下发,2已作废', Mybatis封装之后日志打印也确实 ...