.NET中如何测试Private和Protected方法
2013-10-23 11:31
447 查看
TDD是1)写测试2)写通过这些测试的代码,3)然后重构的实践.在,NET社区中, 这个概念逐渐变得非常流行,这归功于它所增加的质量保证.此时,它很容易测试public方法,但是一个普遍的问题出现了,”我如何测试Protected和private方法呢?”
本文将:
总结”你是否应该测试private方法的争论?”的一些关键点.
创建一些案例,这些案例仍旧是有用的,至少知道怎样测试private和protected方法—不考虑你站在争论的哪一边.
提供方法和可下载的代码示例来展现这些测试技术.
在这些主题的两方,都有明了并且具有经验的人.因此我不打算,也不期望终结”我是否应该测试private方法”的争论.但是对于双方来说,这里仍有价值来知道如何测试他们,即使你认为private不应该被测试.
如果你至少能表现出你可以测试他们,但是你没有这样做(例如,你没有简单的说”不要测试private方法”,因为你不知道如何去测试),你的观点将更加具有说服力.
测试非public方法的选择让你明白在你的小组中,什么真正做的最好.
只要仍有有效的条件,是值得拥有一种方便的方法来测试他们.
自动化(Automatic)
彻底(Thorough )
可重复(Repeatable)
独立(Independent )
专业(Professional)
对于测试private/protected方法来说,有另外三个附加原则:
透明(Transparency) - 不要改变测试下的系统(System Under Test ,SUT),例如,在产品代码中增加包装的方法.
范围(Scope) - 可以在Debug和Release下运行
简单(Simplicity) -最小的开销,因此容易修改,并且非常简单引入最小的风险.
记住这些原则,下面是一些不足的策略.
Pragmatic Unit Testing in C# with NUnit一书解释了一个解决方案:创建一个继承自MyObject类的类MyObjectTester,然后创建一个public方法
方法很简单,也遵循所有原则:
假设我们想测试来自
一个解决方法是创建一个UnitTestUtilities工程,这个工程有一个helper类通过反射来调用测试方法.例如,供下载的解决方案在
Private方法
看看
我们可以在一个NUnit测试中像下面使用这个Utility:
本文将:
总结”你是否应该测试private方法的争论?”的一些关键点.
创建一些案例,这些案例仍旧是有用的,至少知道怎样测试private和protected方法—不考虑你站在争论的哪一边.
提供方法和可下载的代码示例来展现这些测试技术.
背后的方法
你是否应该测试private方法?
一个Google查询 向你展示了有很多关于使用private方法的争议,更不用说测试他们了.下面这个表概括了一些关于这个话题的正方和反方的普遍意见.正方 | 反方 | |
使用private方法 | 封装private方法提供了封装,对于终端客户来说,它使代码更易使用 重构更容易重构private方法,一位他们永远不会直接被外部的客户端调用,因此,修改签名(Signature)不会影响任何方法调用. 验证 不像public方法那样必须验证所有输入,因为他们被外部调用时,private方法在类中安全调用,不需要同样严格的验证—输入应该在public方法中已经验证了. 测试范围暴露每个方法为public,将在很大程度上增加测试的范围.private方法仅仅使用在开发者如何去使用他们,然而public方法需要测试每种可能,这就需要一个更广阔的测试范围了. | 不能重构如果一个类足够复杂,值得使用private方法,那么它需要重构. 隐藏功能性private方法(如果正确设计)提供有用的客户端可以访问的特性,那么任何private方法都值得测试,并且应该真正为public. |
测试Private方法 | 测试控制private方法可以包含复杂的逻辑,并且它可以增加测试控制来直接访问方法,测试它,来代替通过一个public方法间接访问它. 原则单元测试是测试最小的功能代码片断.private方法是功能型代码片断,因此,基于原则,private方法应该是可测试的. | 已经覆盖了仅仅只有public接口才可以测试.private已经测试了, 它是通过测试的public方法调用来完成的. 脆弱的代码如果你重构代码,操作private方法,并且如果你有和这些private相关的测试,你同时也需要操作这些测试. |
如果你至少能表现出你可以测试他们,但是你没有这样做(例如,你没有简单的说”不要测试private方法”,因为你不知道如何去测试),你的观点将更加具有说服力.
测试非public方法的选择让你明白在你的小组中,什么真正做的最好.
只要仍有有效的条件,是值得拥有一种方便的方法来测试他们.
好的原则以及不适当的技术
Andrew Hunt a和 David Thomas在他们的书中Pragmatic Unit Testing in C# with NUnit, 解释到,好的单元测试是ATRIP:自动化(Automatic)
彻底(Thorough )
可重复(Repeatable)
独立(Independent )
专业(Professional)
对于测试private/protected方法来说,有另外三个附加原则:
透明(Transparency) - 不要改变测试下的系统(System Under Test ,SUT),例如,在产品代码中增加包装的方法.
范围(Scope) - 可以在Debug和Release下运行
简单(Simplicity) -最小的开销,因此容易修改,并且非常简单引入最小的风险.
记住这些原则,下面是一些不足的策略.
策略 | 问题 |
不要使用任何private方法. | 它避免这个问题 |
使用指示符#if DEBUG ...#endif来包装一个public方法,这个方法然后包装private方法.单元测试现在可以间接访问那些public方法包装的private方法.(这是一种我使用许多次的方法,并且发现它是单调的,不是面向对象的) | 只在Debug下工作. 它是一个过程,而不是面向对象.我们需要在产品代码和单元测试中包装单独的方法. 通过增加public方法,会修改SUT. |
Public方法使用[Conditional("DEBUG" )] 属性包装 private方法. | 只在Debug下工作. |
创建内部方法来访问private方法.然后在public方法包装那些private方法的程序集的其他地方,创建一个公共的测试类. | 通过增加内部钩子,以及最后使得private方法在产品中可用,会改编发布代码. 这需要很多额外得编码,因此使脆弱得. |
测试Protected方法
Protected方法仅仅对于它得继承类可见,因此,对于测试套件来说并不是立即可见的.例如,激射我们想测试来自fromClassLibrary1.MyObject的方法.
protected string MyProtectedMethod(string strInput, int i32Value) { return this.Name + ": " + strInput + ", " + i32Value.ToString(); }
Pragmatic Unit Testing in C# with NUnit一书解释了一个解决方案:创建一个继承自MyObject类的类MyObjectTester,然后创建一个public方法
TestMyProtectedMethod
,
这个方法包装了那个
protected
方法
.
例如
,
public new string TestMyProtectedMethod(string strInput, int i32Value) { return base.MyProtectedMethod(strInput, i32Value); }
方法很简单,也遵循所有原则:
原则 | 实现 |
透明 | 通过使用继承,并把MyObjectTester 类放入 UnitTests程序集中,它不需要增加任何新的代码到产品程序集中. |
范围 | 在本方法中没有任何东西依赖Debug-only技术. |
简单 | 尽管这个方法需要一新的类,以及每个protected 方法的额外public包装方法,但是它是面向对象的,并且使类型安全的. |
测试Private方法
测试private方法需要多做有些工作,但是我们仍可以使用System.Reflection来实现.你可以使用反射来动态访问一种类型的方法, 包括实例和静态private方法的方法.注意访问private方法需要ReflectionPermission,但是对于运行在开发机器或者构建服务器上的单元测试来说,这不是问题.假设我们想测试来自
ClassLibrary1.MyObject的private方法
MyPrivateMethod:
private string MyPrivateMethod(string strInput, DateTime dt, double dbl) { return this.Name + ": " + strInput + ", " + dt.ToString() + ", " + dbl.ToString(); }
一个解决方法是创建一个UnitTestUtilities工程,这个工程有一个helper类通过反射来调用测试方法.例如,供下载的解决方案在
UnitTestUtilities.Helper中有如下方法:
public static object RunStaticMethod(System.Type t, string strMethod, object [] aobjParams) { BindingFlags eFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; return RunMethod(t, strMethod, null, aobjParams, eFlags); } //end of method public static object RunInstanceMethod(System.Type t, string strMethod, object objInstance, object [] aobjParams) { BindingFlags eFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; return RunMethod(t, strMethod, objInstance, aobjParams, eFlags); } //end of method18private static object RunMethod(System.Type t, string strMethod, object objInstance, object [] aobjParams, BindingFlags eFlags) { MethodInfo m; try { m = t.GetMethod(strMethod, eFlags); if (m == null) { throw new ArgumentException("There is no method '" + strMethod + "' for type '" + t.ToString() + "'."); } object objRet = m.Invoke(objInstance, aobjParams); return objRet; } catch { throw; } } //end of method
Private方法
RunMethod带有一些必要的参数,这些参数是反射需要用来调用一个方法,然后返回值的.它有两个public方法
RunStaticMethod和
RunInstanceMethod来为静态和实例方法分别包装这.
看看
RunMethod,它首先得到类型的
MethodInfo.
因为我们期望它仅为已经存在的方法调用
.
一个空的方法触发一个
Exception
.
一旦我们有
MethodInfo,我们就可以调用实例化对象提供的方法(static 方法为null)以及参数数组.
我们可以在一个NUnit测试中像下面使用这个Utility:
[Test] public void TestPrivateInstanceMethod() { string strExpected = "MyName: Hello, 5/24/2004 12:00:00 AM, 2.1"; ClassLibrary1.MyObject objInstance = new MyObject("MyName"); object obj = UnitTestUtilities.Helper.RunInstanceMethod( typeof(ClassLibrary1.MyObject), "MyPrivateMethod", objInstance, new object[3] {"Hello", new DateTime(2004,05,24), 2.1}); string strActual = Convert.ToString(obj); Assert.AreEqual(strExpected,strActual);18}
原则 | 实现 |
透明 | 我们仅创建的多余代码;UnitTestUtilities,它没有带到产品中. |
范围 | 在本方法中没有任何东西依赖Debug-only技术. |
简单 | Because the method is being dynamically called, the parameters aren't checked at compile time.本方法可以通过一个简单的调用来调用任何方法.一旦你有UnitTestUtilities,你唯一要完成的是为 RunInstanceMethodor RunStaticMethod创建正确的参数(方法名,数据类型,等…),因为方法动态的被调用,参数在编译的时候不会得到检查. |
总结
关于是否应该测试private方法仍有争论,但是我们有能力去测试他们.我们可以使用继承创 建一个继承类TesterClass来测试protected方法.这个继承类包装了其基类的 protected方法为public.我们可以是哦女冠反射来测试private方法,它能够抽象 到一个
UnitTestUtilityhelper类.这些技术都能帮助你改进测试覆盖面.
相关文章推荐
- NUnit详细使用用法(补充)--在.NET中如何利用NUnit测试Private和Protected方法
- NUnit详细使用用法(补充)--在.NET中如何利用NUnit测试Private和Protected方法
- NUnit详细使用用法(补充)--在.NET中如何利用NUnit测试Private和Protected方法
- 使用 nUnit 测试 Private 和 Protected 方法
- 测试 Java 类的private和protected变量和方法
- 在MS Test中如何测试private方法
- 关于php中反射类的简单理解:如何拿到一个类的所有方法(public+protected+private)
- 如何测试私有 Private/Internal 方法
- 如何使用NUnit测试类中的private和protected方法
- 如何对类中的private方法进行测试?
- 一个类如何使用另一个类的private、protected方法
- .Net 中如何测试静态方法
- 使用 nUnit 测试 Private 和 Protected 方法
- 测试 Java 类的private和protected变量和方法
- Private私有派生或Protected保护派生时,如何让基类的方法在派生类外面可用
- 如何测试端口通不通(四种方法)
- win7系统中的宋体如何更改(经测试不错的方法)
- 第4代白盒测试方法实践之“如何在VcTester集成自动构建功能”
- PHP实现在对象之外访问其私有属性private及保护属性protected的方法
- 老李分享:Robotium编写测试用例如何模拟Junit4的BeforeClass和AfterClass方法2 - SingleLaunchActivityTestCase