2009-01-08 71 views
8

许多开发人员认为测试私有方法是一个坏主意。然而,我发现的所有例子都基于私有方法是私有的想法,因为调用它们可能会破坏内部对象的状态。但这不仅是隐藏方法的理由。单元测试私有方法:门面模式

让我们考虑Facade模式。我的班级用户需要2种公共方法。他们会太大。在我的例子中,他们需要从数据库的BLOB中加载一些复杂的结构,解析它,填充一些临时COM对象,运行用户宏来验证和修改这些对象,并将修改后的对象序列化为XML。单个metod的相当大的功能:-)这两个公共方法都需要这些操作。所以,我创建了大约10个私有方法,并且有2个公有方法调用它们。其实,我的私人方法不一定是私人的,他们不会破坏实例的内部状态。但是,当我不惯于测试私有方法,我有以下问题:

  1. 发布它们意味着用户的复杂性(他们有他们不需要选择)
  2. 我无法想象TDD风格对于这样一个大型的公共方法,当你要编写500多行代码只是为了返回一些东西(甚至不是真正的结果)。
  3. 从数据库中检索这些方法的数据,并且测试数据库相关的功能要困难得多。

当我测试的私有方法:

  1. 我不发表会混淆用户的详细信息。公共接口包括2种方法。
  2. 我可以工作在TDD风格(一步一步写小方法)。
  3. 我可以使用测试数据覆盖大部分类的功能,无需连接数据库。

可能有人形容,我究竟做错了什么?我应该使用什么样的设计来获得相同的奖金,而不要测试私人方法?

更新:在我看来,我已经提取了一切,我能够到另一个类。所以,我无法想象我可以提取什么。从数据库加载由ORM层执行,解析流,序列化为XML,运行宏 - 一切都由独立类完成。这个类包含相当复杂的数据结构,搜索和转换的例程,并且需要所有提到的实用程序。所以,我认为别的东西是不能提取的;否则,它的责任(关于数据结构的知识)将在类之间分配。

所以,最好的方法来解决我看到现在分成两个对象(门面本身和实物,与私有方法成为大众)和移动真实物体的某个地方,没有人会尝试找到它。在我的情况下(Delphi)它将是一个独立的单元,在其他语言中它可能是一个单独的名称空间。其他类似的选项是2接口,感谢您的想法。

+1

它看起来像已经有一个相同的问题,这一个:http://stackoverflow.com/questions/250692/how-do-you- unit-test-private-methods – 2009-01-08 20:22:28

+0

不是它的笨蛋 – krosenvold 2009-01-08 20:39:17

回答

17

我认为你把太多的责任(实现)放在门面。我通常会认为这是其他类中实际实现的前端。

因此,在你的门面私有方法很可能是在一个或多个其他类的公共方法。然后你可以在那里测试它们。

3

私有方法用于封装具有您正试图测试类外没有任何意义的一些行为。您不应该测试私有方法,因为只有同一类的公共或受保护方法才会调用私有方法。

这可能只是因为你的班级非常复杂,并且需要花费大量精力来测试它。但是,我建议你寻找抽象概念,你可以将它们分解到自己的课堂中。这些类将具有较小的项目范围和测试复杂性。

2

我不熟悉您的要求&设计,但似乎您的设计是程序性的而不是面向对象的。即你有2个公共方法和许多私人方法。如果你将课堂分解为每个对象都有其角色的对象,那么测试每个“小”类将会更容易。另外,你可以设置“helpers”对象的访问级别为package(默认在Java中,我知道在C#中有类似的访问级别),这样你不会在API中公开它们,但你可以单独测试它们他们是单位)。

4

难道有人会说,我的 做错了什么?

也许什么都没有?

如果我想测试一个方法,我使它成为默认(包)范围并测试它。

你已经提到了另一个很好的解决方案:用你的两种方法创建一个接口。您的客户访问这两种方法,其他方法的可见性无关紧要。

2

也许如果你需要时间,并从Miško看 Clean Code Tech talks。他非常了解如何编写代码以便进行测试。

2

这是一个有点争议的话题......大多数TDD持有人认为,重构你的方法更简单的单元测试实际上使你的设计更好。我认为这通常是正确的,但公共API的私有方法的具体情况绝对是一个例外。所以,是的,你应该测试私人方法,不,你不应该公开。

如果你在Java中工作,这是我写的,这将有助于你在一个类测试静态私有方法的实用程序方法:

import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import junit.framework.Assert; 

public static Object invokeStaticPrivateMethod(Class<?> clazz, String methodName, Object... params) { 
    Assert.assertNotNull(clazz); 
    Assert.assertNotNull(methodName); 
    Assert.assertNotNull(params); 

    // find requested method 
    final Method methods[] = clazz.getDeclaredMethods(); 
    for (int i = 0; i < methods.length; ++i) { 
     if (methodName.equals(methods[i].getName())) { 
      try { 
       // this line makes testing private methods possible :) 
       methods[i].setAccessible(true); 
       return methods[i].invoke(clazz, params); 
      } catch (IllegalArgumentException ex) { 
       // maybe method is overloaded - try finding another method with the same name 
       continue; 
      } catch (IllegalAccessException ex) { 
       Assert.fail("IllegalAccessException accessing method '" + methodName + "'"); 
      } catch (InvocationTargetException ex) { 
       // this makes finding out where test failed a bit easier by 
       // purging unnecessary stack trace 
       if (ex.getCause() instanceof RuntimeException) { 
        throw (RuntimeException) ex.getCause(); 
       } else { 
        throw new InvocationException(ex.getCause()); 
       } 
      } 
     } 
    } 

    Assert.fail("method '" + methodName + "' not found"); 
    return null; 
} 

这很可能对非静态方法被改写为好,但这些讨厌的私人方法通常是私人的,所以我从不需要这些。 :)

2

假设你有8个私人方法和2个公共方法。如果你可以独立执行一个私有方法,即而不调用任何其他方法,并且没有状态损坏副作用,那么单元测试就是这个方法是有意义的。但在那种情况下,不需要该方法是私人的!

在C#我会做保护的,而不是私人这样的方法,并且将它们公开为公众在子类中进行测试

给你的情况下,它可能会更有意义的检验的方法进行公开,并让用户有一个真正的外观只有他们需要的两种公共方法,他们的界面