通过公共接口访问内部方法的测试可Mock化改造问题咨询
这确实是个常见的两难问题——既要靠接口抽象来方便Mock,又不想放弃同程序集内对内部方法的访问权限。我给你几个实用的解决方案,你可以根据项目的实际情况来选:
新增内部接口扩展
既然MyClass本来就实现了主接口,你可以在同一个程序集里定义一个内部接口(比如IMyClassInternal),把InternalMethod放到这个接口里,然后让MyClass同时实现这个内部接口。示例代码:// 同程序集内的内部接口 internal interface IMyClassInternal { void InternalMethod(); } public class MyClass : IMyMainInterface, IMyClassInternal { public void PublicMethod() { /* 主接口实现逻辑 */ } internal void InternalMethod() { /* 原有内部方法逻辑 */ } }当你在同程序集内需要调用内部方法时,只需要把容器里的
IMyMainInterface实例强制转换成IMyClassInternal就行:((IMyClassInternal)containerClass.MyClassInstance).InternalMethod();优点是完全保留了封装性,测试时Mock主接口不受任何影响;缺点是需要多维护一个轻量的内部接口,不过额外成本很低。
用反射访问内部方法(测试场景友好)
如果只是在测试代码里需要调用这个内部方法,反射是个快速见效的办法。你可以写个简单的辅助方法来获取并调用内部方法:public static void CallInternalMethod(object instance) { var method = instance.GetType().GetMethod( "InternalMethod", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic ); method?.Invoke(instance, null); }优点是不用改动原有代码结构;缺点是反射会有轻微的性能开销,而且属于“绕过”封装的做法,只建议在测试场景下使用。
允许测试程序集访问内部成员
如果你的测试需要频繁调用内部方法,且不想用反射,可以在MyClass所在程序集的AssemblyInfo.cs里添加[InternalsVisibleTo]属性,将测试程序集标记为可信:[assembly: InternalsVisibleTo("YourTestAssemblyName")]这样测试程序集里就能直接访问MyClass的内部方法了——甚至你可以在测试中把Mock的
IMyMainInterface对象转换为MyClass(主流Mock框架比如Moq支持用As<MyClass>来实现)。这个做法会让内部成员对测试程序集完全可见,虽然稍微破坏了封装,但在测试场景下是非常常用的方案。重构业务逻辑(长远最优解)
如果内部方法的存在本身就导致了接口抽象的矛盾,你可以考虑重构业务逻辑:比如把InternalMethod的核心逻辑抽离到一个独立的服务类中,或者如果业务上合理的话,把这个方法的功能拆分成主接口里的公共方法。这样既保持了接口的纯净性,又从根源上解决了内部方法的访问问题。当然这个方案需要改动原有代码结构,适合有充足重构空间的场景。
内容的提问来源于stack exchange,提问作者Kyle G.

