我正在构建一个类库,它将有一些公共和私有方法。我希望能够对私有方法进行单元测试(主要是在开发过程中,但也可以用于将来的重构)。

正确的做法是什么?


当前回答

我想在这里创建一个清晰的代码示例,您可以在任何想要测试私有方法的类上使用它。

在您的测试用例类中只包括这些方法,然后按照指示使用它们。

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$this->_callMethod('_someFunctionName', array(param1,param2,param3));

只需要按照它们在原始私有函数中出现的顺序发出参数

其他回答

这里有一篇关于私有方法单元测试的好文章。但是我不知道哪个更好,是让你的应用程序专门为测试而设计(就像只为测试而创建测试)还是使用反射来测试。 我敢肯定大多数人都会选择第二种方式。

我还使用了InternalsVisibleToAttribute方法。值得一提的是,如果你为了达到这个目的而将你之前的私有方法变成内部方法,那么也许它们不应该成为直接单元测试的对象。

毕竟,您是在测试类的行为,而不是它的具体实现——您可以在不更改前者的情况下更改后者,并且您的测试仍然应该通过。

如果您想对私有方法进行单元测试,那么可能会出现一些错误。单元测试(一般来说)用于测试类的接口,即类的公共(和受保护)方法。你当然可以“破解”一个解决方案(即使只是通过公开方法),但你可能还想考虑:

如果您想测试的方法确实值得测试,那么将它移到自己的类中可能是值得的。 向调用私有方法的公共方法添加更多测试,测试私有方法的功能。(正如评论者所指出的,只有当这些私有方法的功能确实是公共接口的一部分时,才应该这样做。如果它们实际上执行对用户隐藏的功能(即单元测试),这可能是不好的)。

您可以在Visual studio 2008中为私有方法生成测试方法。当您为私有方法创建单元测试时,test References文件夹将添加到测试项目中,访问器将添加到该文件夹中。存取器也在单元测试方法的逻辑中被引用。这个访问器允许单元测试调用正在测试的代码中的私有方法。 详情请看

http://msdn.microsoft.com/en-us/library/bb385974.aspx

我认为应该问的一个更基本的问题是,为什么要首先测试私有方法。这是一种代码气味,你试图通过类的公共接口测试私有方法,而该方法是私有的,因为它是一个实现细节。人们应该只关心公共接口的行为,而不是它在背后是如何实现的。

如果我想测试私有方法的行为,通过使用公共重构,我可以将其代码提取到另一个类中(可能具有包级可见性,以确保它不是公共API的一部分)。然后我可以单独测试它的行为。

重构的产物意味着私有方法现在是一个独立的类,它已经成为原始类的合作者。通过它自己的单元测试,它的行为将被很好地理解。

然后,当我试图测试原始类时,我可以模拟它的行为,这样我就可以集中精力测试该类公共接口的行为,而不必测试公共接口的组合爆炸及其所有私有方法的行为。

我认为这类似于开车。当我开车时,我不会把引擎盖打开,这样我就能看到发动机在工作。我依靠汽车提供的接口,即转速计数器和速度计来知道发动机是否在工作。我依靠的是当我踩下油门踏板时,汽车实际上在移动。如果我想测试引擎,我可以单独检查它。: D

当然,如果您有遗留应用程序,直接测试私有方法可能是最后的手段,但我更希望对遗留代码进行重构,以实现更好的测试。迈克尔·费瑟就这个主题写了一本很棒的书。http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052