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

C#单元测试:不可修改原类时构造函数调用报错解决方案

单元测试绕过基类构造函数认证依赖的实现方案

问题背景

以下代码来自主项目引用的NuGet包,主项目通过认证逻辑调用其中的MyClass类,GetMyAppStoreConfig()方法仅在主项目携带认证上下文发起调用时可正常运行。

public class MyClass : MyClassBase
{
    private SomeClass1 someClass1;
    private MyAppStore myAppStore; // 新增字段
    
    public MyClass(params object[] args)
        : base(args)
    {
        this.someClass1 = new SomeClass1();
        this.myAppStore = new MyAppStore(GetMyAppStoreConfig()); // 新增初始化逻辑
    }

    protected override async Task<SomeValues> Process(
        SomeClass2 someClass2,
        SomeClass3 someClass3,
        SomeClass4 someClass4
    )
    {
        // 业务逻辑实现
    }
 
    // 新增方法,依赖myAppStore字段
    protected override async Task<SomeValues> ProcessWithAuth(
        SomeClass5 someClass5
    )
    {
        // 带认证逻辑的业务实现
    }

    private AppStoreConfig GetMyAppStoreConfig(){
        // 依赖认证上下文的配置获取逻辑
    }
}

原有针对该类的单元测试代码如下:

[TestClass()]
public class MyClassTests : MyClass 
{
    [TestMethod()]
    public void test1()
    {   
        // 测试逻辑
        // 内部调用this.Process(someClass2,someClass3,someClass4)
    }
}

现存问题

当前测试无法正常运行,实例化测试类时会在GetMyAppStoreConfig()方法处抛出异常。本次测试暂不需要覆盖ProcessWithAuth()方法,需要在满足以下约束的前提下调整测试用例,让测试正常运行:

  • 无法为MyClass新增额外构造函数,也不可修改MyClass.cs中任何成员的访问修饰符
  • 测试需要运行Process()方法中的真实业务逻辑,不可直接对该方法进行Mock处理

实现方案

异常触发的核心原因是:测试类实例化时默认调用基类MyClass的构造函数,构造函数中执行的GetMyAppStoreConfig()依赖运行时认证上下文,单元测试环境不满足该条件导致报错。
由于待测试的Process()方法仅依赖私有字段someClass1,完全不涉及新增的myAppStore字段及相关认证逻辑,可以通过跳过构造函数创建实例+反射初始化必要依赖的方式解决,具体步骤如下:

  1. 调用System.Runtime.Serialization.FormatterServices.GetUninitializedObject()创建MyClass实例,该方法会直接分配类型对应的内存空间,不会执行任何构造函数逻辑,从根源避免触发GetMyAppStoreConfig()调用
  2. 通过反射获取MyClass的私有字段someClass1,为其赋值new SomeClass1()实例,满足Process()方法运行的依赖要求
  3. 由于Process()方法不访问myAppStore字段,无需对该字段做额外处理,直接通过反射调用Process()方法即可执行完整的真实业务逻辑

调整后的测试代码示例:

using System.Reflection;
using System.Runtime.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass()]
public class MyClassTests
{
    [TestMethod()]
    public async Task test1()
    {   
        // 跳过构造函数创建MyClass实例
        MyClass instance = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass));
        
        // 反射获取私有字段someClass1并初始化
        FieldInfo someClass1Field = typeof(MyClass).GetField("someClass1", 
            BindingFlags.Instance | BindingFlags.NonPublic);
        someClass1Field.SetValue(instance, new SomeClass1());
        
        // 反射获取受保护的Process方法
        MethodInfo processMethod = typeof(MyClass).GetMethod("Process", 
            BindingFlags.Instance | BindingFlags.NonPublic);
        
        // 构造测试入参
        var someClass2 = new SomeClass2();
        var someClass3 = new SomeClass3();
        var someClass4 = new SomeClass4();
        
        // 调用真实Process方法执行业务逻辑
        SomeValues result = await (Task<SomeValues>)processMethod.Invoke(instance, 
            new object[] { someClass2, someClass3, someClass4 });
        
        // 后续断言逻辑
    }
}

注意:如果MyClassBase基类的Process方法依赖基类中初始化的其他字段,可通过同样的反射方式对必要字段进行赋值,仅需跳过myAppStore相关的初始化逻辑即可。


内容的提问来源于stack exchange,提问作者Saif Haider

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 11:45:37