.NET 4.8下Microsoft Fakes Shim导致单例构造未执行的问题求助
解决Microsoft Fakes Shim影响后续测试真实单例的问题
问题核心
你遇到的问题根源在于:Microsoft Fakes对构造函数的Shim替换是全局生效的,再加上KRSampleManager的懒加载单例特性——当UnitTest1用Shim构造了一个未初始化_events的实例后,单例的静态引用就被这个Shim实例占据,后续UnitTest2调用真实单例时,不会再触发真实构造函数,导致Dispose时执行_events.Clear()抛出空引用异常。
具体解决方案
1. 测试后重置Shim配置
在每个使用Shim的测试用例的清理方法中,重置Shim的全局配置,确保构造函数的替换仅作用于当前测试:
[TestCleanup] public void TestCleanup() { // 重置所有类型的Shim配置 ShimConfiguration.Clear(); // 或者仅重置目标类型的Shim,更精准 ShimKRSampleManager.ClearShims(); }
2. 重置单例的静态状态
即使Shim配置被清理,单例的静态字段仍会保留之前的Shim实例,需要通过反射重置单例的私有静态变量,让下一个测试能重新触发真实构造函数:
[TestCleanup] public void TestCleanup() { ShimConfiguration.Clear(); // 反射获取并重置单例的静态字段 var singletonField = typeof(KRSampleManager).GetField("_instance", BindingFlags.NonPublic | BindingFlags.Static); singletonField?.SetValue(null, null); }
3. 测试框架级别的隔离(可选)
如果上述方法仍有问题,可以让每个测试类运行在独立的AppDomain中(MS Test可通过配置实现),彻底隔离测试间的全局状态,但这种方式会增加测试执行开销,适合极端场景。
4. 设计层面优化(从根源避免)
将单例的创建逻辑抽象为工厂模式,让SampleHandler依赖工厂而非直接依赖单例,测试时通过替换工厂实现隔离:
// 抽象工厂接口 public interface IKRSampleManagerFactory { KRSampleManager GetInstance(); } // 默认工厂实现 public class DefaultKRSampleManagerFactory : IKRSampleManagerFactory { public KRSampleManager GetInstance() => KRSampleManager.Instance; } // 修改SampleHandler依赖工厂 public class SampleHandler { private readonly KRSampleManager _manager; public SampleHandler(IKRSampleManagerFactory factory) { _manager = factory.GetInstance(); } }
UnitTest1可Shim工厂返回模拟实例,UnitTest2使用真实工厂,完全避免全局状态污染。
关于测试顺序/分项目生效的原因
- 调整测试顺序时,UnitTest2先执行会初始化真实单例,后续Shim无法覆盖已初始化的静态引用;
- 分属不同测试项目时,每个项目运行在独立AppDomain中,Shim的全局配置不会跨域影响。
内容的提问来源于stack exchange,提问作者Kushal Raja
相关产品推荐
相关产品推荐

