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

正确的做法是什么?


当前回答

我很惊讶还没有人说过这一点,但我采用的一个解决方案是在类内部创建一个静态方法来测试自身。这使您可以访问用于测试的所有公共和私有内容。

此外,在脚本语言(具有OO功能,如Python、Ruby和PHP)中,您可以在运行时对文件本身进行测试。确保您的更改不会破坏任何东西的快速方法。这显然为测试所有类提供了可伸缩的解决方案:只需运行所有类即可。(你也可以用void main在其他语言中这样做,它也总是运行它的测试)。

其他回答

将它们声明为内部的,然后使用InternalsVisibleToAttribute允许单元测试程序集看到它们。

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

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

  /**
   *
   * @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));

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

在我看来,你应该只对类的公共API进行单元测试。

为了对方法进行单元测试,将其设为公共,会破坏封装,暴露实现细节。

一个好的公共API解决了客户端代码的直接目标,并且完全解决了这个目标。

如果你正在使用。net,你应该使用InternalsVisibleToAttribute。

我倾向于不使用编译器指令,因为它们很快就会把事情弄得乱七八糟。如果你真的需要它们,一种缓解它的方法是把它们放在一个partial类中,并让你的构建在制作产品版本时忽略那个.cs文件。