You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:02:11