.NET 6内部CQRS命令处理器单元测试:移除DynamicProxyGenAssembly2方法
关于内部CQRS命令处理器单元测试的权限问题解答
一、InternalsVisibleTo("DynamicProxyGenAssembly2")生效的原因
大部分.NET测试模拟框架(比如Moq)底层依赖Castle DynamicProxy生成代理类,这些动态生成的代理类会被放到名为DynamicProxyGenAssembly2的临时程序集中。
当你需要为internal的CreateQueryCommandHandler注入ILogger<CreateQueryCommandHandler>时,代理类需要访问CreateQueryCommandHandler这个内部类型的元数据(因为它是泛型ILogger<T>的类型参数)。虽然你已经将内部类权限开放给了测试项目,但动态代理所在的DynamicProxyGenAssembly2程序集并未被授权访问当前项目的内部成员。添加该属性后,CLR会允许这个动态生成的程序集访问当前项目的内部类型,从而解决Castle创建代理时的权限错误。
二、移除该属性的可行方法
既然问题根源是动态代理需要访问内部类型,那可以通过绕开动态代理的方式来移除这个属性:
- 使用.NET官方提供的空日志实现:直接注入
NullLogger<CreateQueryCommandHandler>.Instance(来自Microsoft.Extensions.Logging.Abstractions包),这是.NET自带的无操作Logger实现,不需要生成代理,自然不会触发权限问题。 - 手动编写测试用Logger替身:自己实现一个简单的
ILogger<CreateQueryCommandHandler>,比如只记录日志到内存或控制台,完全不需要依赖动态代理框架。 - 调整模拟框架的使用方式:如果必须用模拟框架,避免对带有内部泛型参数的依赖生成代理。比如对
ILogger接口而非ILogger<CreateQueryCommandHandler>进行模拟(需注意类型匹配问题),或者使用框架提供的无代理创建方式(部分框架支持直接生成静态替身)。
三、更优的单元测试策略
针对内部CQRS处理器的测试,建议遵循以下策略来减少权限问题并提升测试质量:
- 聚焦核心业务逻辑:Logger这类辅助依赖通常不影响业务结果,优先使用
NullLogger或手动替身,无需模拟其行为,把测试精力放在验证处理器的业务输出上。 - 使用官方测试替身:.NET生态提供了很多开箱即用的测试替身(比如
NullLogger、MemoryRepository等),这些实现无需动态代理,避免了权限和依赖问题。 - 统一管理测试依赖:在测试项目中创建一个静态类或测试基类,统一封装内部处理器的依赖注入配置,比如预先注册好所有需要的替身依赖,每个测试用例直接复用,减少重复代码。
- 避免过度模拟:只模拟那些会直接影响测试结果的依赖(比如数据访问接口、外部服务客户端),对于无状态的辅助依赖,优先使用真实的轻量级实现而非模拟。
内容的提问来源于stack exchange,提问作者Hardik Dhuri
相关产品推荐
相关产品推荐

