使用NSubstitute创建实例时如何避免调用目标类的基类构造函数
核心原因
.NET 运行时的类型实例化规则要求,任何子类实例创建时都会沿继承链依次执行父类构造函数,这个逻辑无法通过 NSubstitute 的配置直接跳过。你提到的 DoNotCallBase 仅作用于普通类方法的调用逻辑,对构造函数无效。
方案1:配置构造依赖让基类构造正常执行(优先推荐)
如果可以明确 FooBase 构造函数中对 IBar 参数的依赖逻辑,直接提前配置好 IBar 替代实例的对应行为,让基类构造函数可以无异常执行完,即可正常创建部分替代实例:
var mockBar = Substitute.For<IBar>(); // 提前配置 FooBase 构造函数会调用的 IBar 成员的返回值/行为 mockBar.构造函数调用的方法名().Returns(预期返回值); // 正常创建部分替代实例 var partialFoo = Substitute.ForPartsOf<Foo>(mockBar); // 后续正常配置其他方法是否调用基类即可 partialFoo.When(x => x.需要跳过基类的方法()).DoNotCallBase();
该方案成本最低,无需额外调整代码结构。
方案2:用适配器模式解耦对不可控类型的直接依赖
如果无法满足基类构造的执行条件,建议通过适配器模式隔离不可控的 Foo 类型,完全绕过基类构造的问题:
- 定义适配接口,声明你需要用到的
Foo的所有公开方法 - 实现适配器类,内部持有真实
Foo实例,转发方法调用 - 测试时直接 Mock 适配接口即可
// 1. 定义适配接口 public interface IFoo { void 你用到的Foo方法1(); int 你用到的Foo方法2(string param); } // 2. 实现适配器 public class FooAdapter : IFoo { private readonly Foo _realFoo; public FooAdapter(Foo realFoo) => _realFoo = realFoo; public void 你用到的Foo方法1() => _realFoo.你用到的Foo方法1(); public int 你用到的Foo方法2(string param) => _realFoo.你用到的Foo方法2(param); } // 3. 测试时直接Mock接口,完全不需要实例化Foo var mockFoo = Substitute.For<IFoo>(); mockFoo.你用到的Foo方法2("test").Returns(100);
该方案符合面向接口的设计原则,后续不受 Foo 或 FooBase 内部变更的影响,维护成本最低。
方案3:用反射跳过构造函数实例化(不推荐,仅当前两种方案不可行时使用)
如果一定要直接操作 Foo 实例且无法执行基类构造,可以使用 .NET 序列化相关的 FormatterServices 类,不调用任何构造函数创建对象实例:
using System.Runtime.Serialization; using System.Reflection; // 不调用任何构造函数创建Foo实例 var uninitializedFoo = (Foo)FormatterServices.GetUninitializedObject(typeof(Foo)); // 手动给Foo/FooBase的必要字段赋值(根据实际需要调整) var barField = typeof(FooBase).GetField("_bar", BindingFlags.Instance | BindingFlags.NonPublic); barField?.SetValue(uninitializedFoo, Substitute.For<IBar>());
⚠️ 注意:该方式创建的对象所有字段都是默认值,若后续调用的方法依赖构造函数初始化的状态,会出现不可预期的运行错误,调试成本极高。
内容的提问来源于stack exchange,提问作者lonix
相关产品推荐
相关产品推荐

