C#如何在不修改原有类定义的前提下部分重写类方法用于测试
方案1:使用动态代理库自动生成透传包装类
装饰器维护成本高的问题可以用运行时代理生成工具解决,.NET生态最常用的是Castle DynamicProxy,它可以在运行时自动生成接口的实现类,所有未显式拦截的方法都会自动转发给传入的真实Model实例,不需要手动写透传代码。
示例代码:
// 1. 安装Castle.Core Nuget包 // 2. 定义拦截器,只处理需要重写的方法 public class ModelMethodInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { // 只拦截需要修改的GetOther方法 if (invocation.Method.Name == nameof(IModel.GetOther)) { invocation.ReturnValue = "something for testing"; return; } // 其余方法全部转发给真实实例执行 invocation.Proceed(); } } // 3. 生成代理实例使用 var generator = new ProxyGenerator(); var realModel = new Model(); var testModel = (IModel)generator.CreateInterfaceProxyWithTarget(typeof(IModel), realModel, new ModelMethodInterceptor());
- 优点:不需要维护包装类,接口变更不需要修改任何透传代码,只需要调整拦截逻辑即可
- 适用场景:已经为Model提取了公共接口的场景
方案2:Moq/NSubstitute开启CallBase调用原类实现
常用的Mock库都支持类Mock时未配置的方法调用原类实现,只需要显式开启CallBase开关即可:
Moq示例:
// 注意这里直接Mock具体的Model类,不是IModel接口 var mockModel = new Mock<Model>() { CallBase = true }; // 只配置你要重写的方法(注意该方法需要是virtual的才能被重写) mockModel.Setup(m => m.GetOther()).Returns("something for testing"); // 拿到实例用就行,GetSomething会自动调用原Model的实现 var testModel = mockModel.Object;
- 优点:无需引入额外依赖,语法简洁,不需要写拦截器
- 限制:需要被重写的方法是
virtual修饰的,如果原类方法没有virtual标记无法重写
方案3:运行时方法重写(适用于非virtual方法的场景)
如果原类的方法没有virtual标记,又不愿意修改生产代码,可以用Harmony库在测试运行时动态重写指定方法的实现,完全不需要修改原类代码,也不需要接口:
// 1. 安装Lib.Harmony Nuget包 // 2. 定义补丁,替换GetOther方法的实现 [HarmonyPatch(typeof(Model), nameof(Model.GetOther))] public static class ModelGetOtherPatch { static bool Prefix(ref string __result) { __result = "something for testing"; // 返回false表示不执行原方法的逻辑 return false; } } // 3. 测试启动时应用补丁 var harmony = new Harmony("test.patch"); harmony.PatchAll(); // 之后所有Model实例的GetOther方法都会返回你指定的值,其他方法完全不变
- 优点:不需要原类有接口,不需要方法是virtual,完全无侵入修改生产代码
- 限制:仅建议在测试环境使用,生产环境不推荐这种运行时重写的实现方式
方案4:源生成器自动生成通用装饰器
如果接口变更非常频繁,想要编译期检查的能力,可以用C#源生成器写一个通用的装饰器生成工具,输入指定接口,自动生成透传所有方法的partial装饰器类,你只需要在自己的partial类里重写需要修改的方法即可,接口变更时源生成器会自动更新生成的代码,不会出现遗漏方法的编译错误。
内容的提问来源于stack exchange,提问作者YAC
相关产品推荐
相关产品推荐

