我已经阅读了这篇关于如何测试私有方法的文章。我通常不测试它们,因为我总是认为只测试将从对象外部调用的公共方法更快。你测试私有方法吗?我应该一直测试它们吗?


当前回答

我们通过推断来测试私有方法,我的意思是我们寻找总类测试覆盖率至少为95%,但我们的测试只调用公共方法或内部方法。为了获得覆盖,我们需要根据可能发生的不同场景对公众/内部人员进行多次调用。这使得我们的测试更专注于它们所测试的代码的目的。

特朗皮对你链接的帖子的回答是最好的。

其他回答

对于从测试中调用什么api,公共和私有并不是一个有用的区分,方法和类也不是。大多数可测试单元在一个上下文中是可见的,但在其他上下文中是隐藏的。

重要的是覆盖范围和成本。您需要最小化成本,同时实现项目的覆盖目标(行、分支、路径、块、方法、类、等价类、用例……)不管团队怎么决定)。

因此,使用工具来确保覆盖率,并设计您的测试以减少成本(短期和长期)。

不要让测试变得过于昂贵。 如果只测试公共入口是最便宜的,那就这样做。 如果测试私有方法成本最低,那就这么做。

随着您的经验越来越丰富,您将能够更好地预测何时值得重构以避免测试维护的长期成本。

“我应该测试私有方法吗?”的答案是“有时.......”。通常情况下,您应该针对类的接口进行测试。

其中一个原因是您不需要对一个特性进行双重覆盖。 另一个原因是,如果您更改了私有方法,则必须为它们更新每个测试,即使对象的接口根本没有更改。

这里有一个例子:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

在RefactoredThing中,你现在有5个测试,其中2个你必须为重构而更新,但你的对象的功能实际上没有改变。所以让我们假设事情比这更复杂,你有一些方法来定义输出的顺序,比如:

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

这不应该由外部用户来运行,但是您的封装类可能太笨重了,无法一遍又一遍地运行这么多逻辑。在这种情况下,您可能更愿意将其提取到一个单独的类中,为该类提供一个接口并对其进行测试。

最后,假设你的主对象非常重,方法非常小你需要确保输出是正确的。你会想,“我必须测试这个私有方法!”也许你可以通过传入一些繁重的工作作为初始化参数使你的对象更轻?然后你可以放一些更轻的东西进去测试。

这显然与语言有关。在过去的c++中,我将测试类声明为友类。不幸的是,这需要您的生产代码了解测试类。

是的,我确实测试私有函数,因为尽管它们是由你的公共方法测试的,但在TDD(测试驱动设计)中测试应用程序的最小部分是很好的。但是在测试单元类中不能访问私有函数。下面是我们测试私有方法的方法。

为什么我们有私有方法?

私有函数主要存在于我们的类中,因为我们希望在公共方法中创建可读的代码。 我们不希望这个类的用户直接调用这些方法,而是通过我们的公共方法。此外,我们不希望在扩展类时改变它们的行为(在受保护的情况下),因此它是一个private。

当我们编码时,我们使用测试驱动设计(TDD)。这意味着有时我们会偶然发现一个私有的功能片段并想要进行测试。私有函数在phpUnit中是不可测试的,因为我们不能在Test类中访问它们(它们是私有的)。

我们认为有3个解决方案:

1. 你可以通过你的公共方法来测试你的私处

优势

简单的单元测试(不需要“hack”)

缺点

程序员需要了解公共方法,而他只想测试私有方法 您不是在测试应用程序中最小的可测试部分

2. 如果private是如此重要,那么为它创建一个新的单独的类可能是一个代码味道

优势

你可以把它重构成一个新类,因为如果是这样的话 重要的是,其他类可能也需要它 可测试单元现在是一个公共方法,因此可测试

缺点

如果一个类是不需要的,并且只被 方法来自的类 由于增加的开销而造成潜在的性能损失

3.将访问修饰符更改为(final) protected

优势

您正在测试应用程序中最小的可测试部分。当 使用final protected,函数将不会被重写(只是 像一个私人) 无性能损失 没有额外的开销

缺点

你把一个私有访问权限改成了受保护,也就是说 它的孩子可以接触到 您仍然需要在测试类中使用Mock类

例子

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

因此,我们的测试单元现在可以调用test_sleepWithSuspect来测试之前的私有函数。

我认为最好只测试对象的公共接口。从外部世界的角度来看,只有公共接口的行为是重要的,这是您的单元测试应该指向的方向。

一旦你为一个对象编写了一些可靠的单元测试,你就不希望因为接口背后的实现改变了而不得不回去修改那些测试。在这种情况下,您已经破坏了单元测试的一致性。