如何在C#单元测试中替换构造函数内的Func委托
问题:单元测试中替换构造函数内调用的Func委托
原类代码
internal Func<...> MyFunc { get; set; } public SomeClass(ISomeParam param) : base(param) { MyFunc = MyMethod; MyPropertyX = MyFunc(someOtherParams); }
无效的测试写法
var SubjectUnderTest = new MyClass(testParams) { MyFunc = MyNewMethodInsteadOfThatOneInClass };
这种写法无效的核心原因:对象初始化器的赋值操作是在构造函数完全执行完毕后才执行的。构造函数里调用MyFunc(someOtherParams)时,MyFunc还是被赋值为MyMethod的状态,之后才会替换成你设置的新委托,所以对构造函数内的调用没有任何影响。
可行解决方案
方案1:给构造函数增加重载,支持传入自定义Func
修改原类的构造函数,新增带可选参数的重载,优先使用传入的自定义委托,无传入则用默认的MyMethod:
public SomeClass(ISomeParam param, Func<...> customFunc = null) : base(param) { MyFunc = customFunc ?? MyMethod; MyPropertyX = MyFunc(someOtherParams); }
测试时直接传入自定义委托即可:
var sut = new SomeClass(testParam, MyNewMethodInsteadOfThatOneInClass);
方案2:通过子类重写方法(不修改原构造函数签名)
如果不想改动原有构造函数的API,可以把MyMethod改成虚方法,在测试中创建子类重写该方法:
- 修改原类中的
MyMethod访问修饰符为protected virtual:
protected virtual ReturnType MyMethod(...) { // 原有业务逻辑 }
- 测试项目中创建可测试的子类:
public class TestableSomeClass : SomeClass { private readonly Func<...> _customFunc; public TestableSomeClass(ISomeParam param, Func<...> customFunc) : base(param) { _customFunc = customFunc; } protected override ReturnType MyMethod(...) { return _customFunc(...); } }
- 测试时使用这个子类:
var sut = new TestableSomeClass(testParam, MyNewMethodInsteadOfThatOneInClass);
方案3:反射(不推荐)
如果无法修改原类代码,可尝试用反射替换委托,但这种方式依赖内部实现细节,维护成本高且容易出错,仅作为最后备选:
var sut = new SomeClass(testParam); // 用反射获取MyFunc属性并赋值 var myFuncProperty = typeof(SomeClass).GetProperty("MyFunc", BindingFlags.Instance | BindingFlags.NonPublic); myFuncProperty.SetValue(sut, MyNewMethodInsteadOfThatOneInClass); // 注意:这种方式无法影响构造函数内的MyFunc调用,只能修改构造完成后的委托状态,若要影响构造过程需用更复杂的拦截手段,不推荐常规单元测试使用
内容的提问来源于stack exchange,提问作者JJJ
相关产品推荐
相关产品推荐

