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

如何模拟带有内部构造函数与静态工厂方法的类?

问题:无法模拟带有内部构造函数的第三方服务类

场景重现

我有一个依赖第三方NuGet包/SDK中ABCService的MyService类,代码如下:

public class MyService
{
   private readonly ABCService _abc;

   public MyService(ABCService abc)
   {
      this._abc = abc;
   }

   public async Task Run(string id) 
   {
      // 业务逻辑
      var result = await this._abc.DoSomething(id);
      // 业务逻辑
   }
}

第三方ABCService的结构大致如下:

public class ABCService 
{
   internal ABCService(string someConnectionString, object someSettings)
   {
      // 初始化逻辑
   }

   public static ABCService CreateFromConnectionString(string someConnectionString, object someSettings)
   {
      // 创建逻辑
      return new ABCService(someConnectionString, someSettings);
   }
}

尝试用Moq模拟ABCService时抛出异常:Parent does not have a default constructor.,代码如下:

var mock = new Mock<ABCService>();
var myService = new MyService(mock.Object);

问题原因

Moq等Mock库的核心原理是生成目标类的动态子类来实现模拟逻辑,但ABCService仅暴露了internal级别的构造函数,且没有公开的无参构造函数。动态子类无法访问父类的内部构造函数,也无法调用默认构造函数,导致实例化失败,抛出上述异常。

解决方案

1. 抽象接口(推荐方案)

通过定义抽象接口解耦MyService与具体的ABCService,这也是依赖倒置原则的最佳实践,能从根本上解决第三方类无法模拟的问题。

首先定义与ABCService核心能力匹配的接口:

public interface IABCService 
{
   Task DoSomething(string id);
}

修改MyService,让其依赖接口而非具体类:

public class MyService
{
   private readonly IABCService _abc;

   public MyService(IABCService abc)
   {
      this._abc = abc;
   }

   public async Task Run(string id) 
   {
      // 业务逻辑
      var result = await this._abc.DoSomething(id);
      // 业务逻辑
   }
}

为第三方的ABCService实现这个适配器类,适配接口与具体服务:

public class ABCServiceAdapter : IABCService
{
   private readonly ABCService _innerService;

   public ABCServiceAdapter(string connectionString, object settings)
   {
      _innerService = ABCService.CreateFromConnectionString(connectionString, settings);
   }

   public Task DoSomething(string id)
   {
      return _innerService.DoSomething(id);
   }
}

之后就可以轻松模拟接口:

var mock = new Mock<IABCService>();
// 配置Mock行为,例如指定返回值
mock.Setup(x => x.DoSomething(It.IsAny<string>()))
    .ReturnsAsync("模拟处理结果");

var myService = new MyService(mock.Object);

2. 临时方案(不推荐)

如果暂时无法修改MyService的依赖结构,可以尝试通过反射强制创建ABCService实例,再结合Moq的CallBase特性,但这种方法依赖第三方类的内部实现,稳定性差,仅适合临时场景:

// 通过反射获取内部构造函数
var constructor = typeof(ABCService).GetConstructor(
    BindingFlags.Instance | BindingFlags.NonPublic,
    null,
    new[] { typeof(string), typeof(object) },
    null);

// 创建实例
var abcInstance = constructor.Invoke(new object[] { "模拟连接字符串", new object() }) as ABCService;

// 创建Mock并启用CallBase,保留原类基础逻辑
var mock = new Mock<ABCService>(abcInstance) { CallBase = true };
var myService = new MyService(mock.Object);

注意:此方法存在风险,若第三方ABCService的内部构造函数签名变更,代码会直接失效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 09:10:34