.NET中如何测试Private和Protected方法

 

TDD是1)写测试2)写通过这些测试的代码,3)然后重构的实践.在,NET社区中, 这个概念逐渐变得非常流行,这归功于它所增加的质量保证.此时,它很容易测试public方法,但是一个普遍的问题出现了,”我如何测试Protected和private方法呢?”

本文将:

  • 总结”你是否应该测试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方法”的争论.但是对于双方来说,这里仍有价值来知道如何测试他们,即使你认为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方法仅仅对于它得继承类可见,因此,对于测试套件来说并不是立即可见的.例如,激射我们想测试来自from ClassLibrary1.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,你唯一要完成的是为RunInstanceMethod or RunStaticMethod创建正确的参数(方法名,数据类型,等…),因为方法动态的被调用,参数在编译的时候不会得到检查.

总结

关于是否应该测试private方法仍有争论,但是我们有能力去测试他们.我们可以使用继承创 建一个继承类TesterClass来测试protected方法.这个继承类包装了其基类的 protected方法为public.我们可以是哦女冠反射来测试private方法,它能够抽象 到一个UnitTestUtility helper类.这些技术都能帮助你改进测试覆盖面.

 
 
 

Private和Protected方法的更多相关文章

  1. .NET中如何测试Private和Protected方法

    TDD是1)写测试2)写通过这些测试的代码,3)然后重构的实践.在,NET社区中, 这个概念逐渐变得非常流行,这归功于它所增加的质量保证.此时,它很容易测试public方法,但是一个普遍的问题出现了, ...

  2. 为什么类和接口不能使用private和protected?接口的方法不能使用private、protected、default

    对于java程序员来说,java的访问权限修饰词public.protected.default.private的区别和使用肯定都不是问题,这里也不再啰嗦了,反正度娘一搜就一大把.最近在整理java ...

  3. java利用反射绕过私有检查机制实行对private、protected成员变量或方法的访问

    在java中,如果类里面的变量是声明了private的,那么只能在被类中访问,外界不能调用,如果是protected类型的,只能在子类或本包中调用,俗话说没有不透风的墙.但是可以利用java中的反射从 ...

  4. Java 类、属性、方法修饰符 public、private、protected、default

    Java 中修饰类修饰符:public .default (默认) Java 中修饰类中属性.方法修饰符:public.private.protected.default (默认) 通过 IDEA 创 ...

  5. 浅谈PHP的Public、Protected、Private三种方法的区别

    public:权限是最大的,可以内部调用,实例调用等.protected: 受保护类型,用于本类和继承类调用.private: 私有类型,只有在本类中使用. <?php error_report ...

  6. JavaScript实现类的private、protected、public、static以及继承

    JavaScript中的类 JavaScript实际上是一种弱类型语言,与C++和Java等语言不同.因此,在JavaScript中,没有强调类(class)这一概念,但实际运用中,类还是很重要的,比 ...

  7. C#中public、private、protected、internal、protected internal (转载)

    在C#语言中,共有五种访问修饰符:public.private.protected.internal.protected internal.作用范围如下表:访问修饰符 说明public 公有访问.不受 ...

  8. Java关键字(一) 修饰符private、protected、public和default的作用域

    我们经常用着四种修饰符去修饰变量.方法和类,但是这四种的作用域都一样吗? 其中private和public可能是最多人知道的,但是protected和default可能就不知道其具体的作用域是哪些范围 ...

  9. public,private,protected,以及不写时的差别

    作用域public,private.protected.以及不写时的差别 这四个作用域的可见范围例如以下表所看到的. 说明:假设在修饰的元素上面没有写不论什么訪问修饰符,则表示friendly.不使用 ...

随机推荐

  1. 在四川大学的第二个冠军游戏在线编程:Peter的X

    四川大学线下编程比赛第二题:Peter的X 公布公司: 有 效 期: CSDN 2014-09-27至2015-09-26 难 度 等 级: 答 题 时 长: 编程语言要求: 120分钟 C C++ ...

  2. JavaScript中的try...catch和异常处理

    在JavaScript可以使用try...catch来进行异常处理.例如: try { foo.bar();} catch (e) { alert(e.name + ": " + ...

  3. MongoDB详解学习历程

    MongoDB是一个基于分布式文件存储的数据库,它是介于关系数据库和非关系数据库之间的产品. MongoDB支持的数据结构非常松散,类似json的bjson格式,因此可以存储比较复杂的数据类型.Mon ...

  4. 教你一步一步部署.net免费空间OpenShift系列之四------绑定域名、使用CDN加速

    很抱歉这几天没有时间,有人问我怎么绑定域名的问题也没有答复,下面进入正题,惊闻ASP.Net要开源了,难道.Net春天要来了?不废话,上回书说,部署完毕ASP.Net网站后,直接访问不能访问(嗯,众所 ...

  5. HDU 5055 Bob and math problem(结构体)

    主题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5055 Problem Description Recently, Bob has been think ...

  6. std::move()

    #include <iostream> #include <utility> #include <vector> #include <string> i ...

  7. getch()、getche()和getchar()函数

    原文:getch().getche()和getchar()函数 getch().getche()和getchar()函数(1) getch()和getche()函数 这两个函数都是从键盘上读入一个字符 ...

  8. VS2013调试的时候出现 “检测到在集成的托管管道模式下不适用的 ASP.NET 设置的解决方法”

    在web.config里面添加了下面一句,完美解决 <system.webServer> <validation validateIntegratedModeConfiguratio ...

  9. socket在windows下和linux下的区别

    原文:socket在windows下和linux下的区别 1)头文件 windows下winsock.h/winsock2.h linux下sys/socket.h    错误处理:errno.h 2 ...

  10. Monkey源码分析之事件注入

    本系列的上一篇文章<Monkey源码分析之事件源>中我们描述了monkey是怎么从事件源取得命令,然后将命令转换成事件放到事件队列里面的,但是到现在位置我们还没有了解monkey里面的事件 ...