我应该测试私有方法还是仅测试公共方法? [关闭]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我应该测试私有方法还是仅测试公共方法? [关闭]相关的知识,希望对你有一定的参考价值。

我读过有关如何测试私有方法的this post。我通常不测试它们,因为我一直认为只测试从对象外部调用的公共方法会更快。你测试私人方法吗?我应该经常测试吗?

答案

我没有单元测试私有方法。私有方法是应该为类的用户隐藏的实现细节。测试私有方法会破坏封装。

如果我发现私有方法是巨大的,复杂的或重要的,足以需要自己的测试,我只需将它放在另一个类中并在那里公开(Method Object)。然后我可以轻松地测试现在存在于自己的类中的先前私有但现在公开的方法。

另一答案

我不是这个领域的专家,但是单元测试应该测试行为,而不是实现。私有方法严格地是实现的一部分,因此不应该测试恕我直言。

另一答案

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

特朗普对你所关联的帖子的回答是最好的。

另一答案

如果您正在开发测试驱动(TDD),您将测试您的私有方法。

另一答案

我认为单元测试用于测试公共方法。您的公共方法使用您的私有方法,因此它们也间接地进行测试。

另一答案

我一直在讨论这个问题,特别是在试用TDD的时候。

我发现在TDD的情况下,我认为可以彻底解决这个问题。

  1. Testing private methods, TDD and Test-Driven Refactoring
  2. Test-Driven Development Isn’t Testing

综上所述:

  • 当使用测试驱动的开发(设计)技术时,私有方法应该仅在已经工作和测试的代码的重新分解过程中出现。
  • 根据该过程的本质,从经过彻底测试的功能中提取的任何简单实现功能都将是自我测试的(即间接测试覆盖)。

对我而言,似乎很清楚,在编码的开始部分,大多数方法将是更高级别的功能,因为它们封装/描述了设计。

因此,这些方法将是公开的,并且测试它们将是足够容易的。

私有方法将在一切运行良好之后出现,我们为了可读性和清洁而重新考虑因素。

另一答案

如上所述,“如果你不测试你的私人方法,你怎么知道他们不会破坏?”

这是一个重大问题。单元测试的一个重点是知道事情在何时,何时以及如何破坏。从而减少了大量的开发和QA工作量。如果测试的所有内容都是公开的,那么您就没有诚实的报道和对班级内部的描述。

我发现执行此操作的最佳方法之一是将测试引用添加到项目中,并将测试放在与私有方法并行的类中。放入适当的构建逻辑,以便测试不会构建到最终项目中。

然后,您将拥有测试这些方法的所有好处,您可以在几秒钟内找到问题,而不是几分钟或几小时。

总而言之,是的,单元测试你的私有方法。

另一答案

你不应该。如果您的私有方法具有必须测试的足够复杂性,则应将它们放在另一个类上。保持高凝聚力,一个班级应该只有一个目的。类公共接口应该足够了。

另一答案

如果你不测试你的私人方法,你怎么知道他们不会破坏?

另一答案

这显然取决于语言。在过去使用c ++时,我已经将测试类声明为友元类。不幸的是,这确实需要您的生产代码了解测试类。

另一答案

我理解私有方法被视为实现细节的观点,然后不必进行测试。如果我们不得不在对象之外进行开发,我会坚持这条规则。但是我们,我们是某种受限制的开发人员,他们只是在对象之外开发,只调用他们的公共方法吗?或者我们实际上还在开发那个对象?由于我们不一定要编写外部对象,我们可能不得不将这些私有方法称为我们正在开发的新公共方法。知道私人方法能够抵御一切困难并不是很好吗?

我知道有些人可以回答,如果我们正在开发另一个公共方法到那个对象那么这个应该被测试,就是这样(私有方法可以继续生活而不进行测试)。但是对于对象的任何公共方法也是如此:在开发Web应用程序时,对象的所有公共方法都是从控制器方法调用的,因此可以被视为控制器的实现细节。

那么为什么我们要测试对象呢?因为它真的很难,更不用说不可能确定我们用适当的输入测试控制器的方法,这将触发底层代码的所有分支。换句话说,我们在堆栈中越高,测试所有行为就越困难。私有方法也是如此。

对我来说,私人和公共方法之间的边界是测试时的心理标准。对我更重要的标准是:

  • 是不同地方不止一次调用的方法?
  • 方法是否足够复杂,需要测试?
另一答案

测试的目的是什么?

到目前为止,大多数答案都说私有方法是实现细节,只要公共接口经过充分测试和工作,它们就不会(或至少不应该)起作用。如果您测试的唯一目的是保证公共接口正常工作,这绝对是正确的。

就个人而言,我主要用于代码测试是为了确保未来的代码更改不会导致问题并帮助我调试工作。我发现像公共接口一样彻底地测试私有方法(如果不是这样的话!)进一步推动了这个目的。

考虑一下:你有公共方法A,它调用私有方法B.A和B都使用方法C.C被更改(可能由您,可能是供应商),导致A开始失败其测试。对B进行测试也不是很有用,即使它是私有的,所以你知道问题是在A中使用C,B使用C还是两者兼而有之?

在公共接口的测试覆盖不完整的情况下,测试私有方法也会增加价值。虽然这是我们通常希望避免的情况,但效率单元测试取决于测试发现错误以及这些测试的相关开发和维护成本。在某些情况下,100%测试覆盖率的好处可能被认为不足以保证这些测试的成本,从而在公共接口的测试覆盖范围中产生差距。在这种情况下,对私有方法进行有针对性的测试可能是代码库的一个非常有效的补充。

另一答案

如果我发现私有方法很庞大或复杂或非常重要,需要自己的测试,我只需将它放在另一个类中并在那里公开(Method Object)。然后我可以轻松地测试以前私有但现在公开的方法,它现在存在于自己的类中。

另一答案

如果方法足够重要/足够复杂,我通常会将其“保护”并进行测试。一些方法将保密,并作为公共/受保护方法的单元测试的一部分隐式测试。

另一答案

我看到很多人都在思考:在公共层面进行测试。但这不是我们的QA团队所做的吗?他们测试输入和预期输出。如果作为开发人员我们只测试公共方法,那么我们只是重做QA的工作而不是通过“单元测试”添加任何值。

另一答案

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

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

这是一个例子:

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

这不应该由外部用户运行,但是您的封装类可能会很重,以便一遍又一遍地运行那么多逻辑。在这种情况下,您可能宁愿将其提取到一个单独的类中,为该类提供一个接口并对其进行测试。

最后,假设您的主要对象非常重,并且方法非常小,您确实需要确保输出正确。你在想,“我必须测试这种私人方法!”。也许你可以通过传递一些繁重的工作作为初始化参数来使你的对象更轻?然后你可以传递更轻的东西并对其进行测试。

另一答案

不,你不应该测试私有方法why?,而且流行的模拟框架,如Mockito不支持测试私有方法。

另一答案

一个要点是

如果我们测试以确保逻辑的正确性,并且私有方法携带逻辑,我们应该测试它。不是吗?那么我们为什么要跳过这个呢?

基于方法的可见性编写测试是完全不相关的想法。

反过来

另一方面,在原始类之外调用私有方法是一个主要问题。在某些模拟工具中模拟私有方法也有局限性。 (例如:Mockito)

虽然有一些像Power Mock这样的工具支持它,但这是一个危险的操作。原因是它需要破解JVM才能实现这一点。

可以做的一件事就是(如果你想为私有方法编写测试用例)

将这些私有方法声明为受保护。但是对于几种情况可能不方便。

另一答案

我从不理解单元测试的概念,但现在我知道它的目标是什么。

单元测试不是一个完整的测试。因此,它不是QA和手动测试的替代品。 TDD在这方面的概念是错误的,因为您无法测试所有内容,包括私有方法,还有使用资源的方法(尤其是我们无法控制的资源)。 TDD的基础是它的所有质量都是无法实现的。

单元测试更多是一个枢轴测试你标记一些任意的枢轴,枢轴的结果应该保持不变。

另一答案

它不仅涉及公共或私有方法或功能,而是关于实现细节。私有函数只是实现细节的一个方面。

毕竟,单元测试是一种白盒测试方法。例如,无论是谁使用覆盖率分析来识别到目前为止在测试中被忽略的代码部分,都会进入实现细节。

A)是的,您应该测试实施细节:

考虑一个排序函数,出于性能原因,如果有多达10个元素,则使用BubbleSort的私有实现,如果有超过10个元素,则使用不同排序方法的私有实现(比如,heapsort)。公共API是排序函数的API。但是,您的测试套件更好地利用了实际使用两种排序算法的知识。

在这个例子中,您肯定可以在公共API上执行测试。然而,这将需要具有多个测试用例,这些测试用例执行具有多于10个元素的排序函数,使得堆测试算法得到充分测试。仅存在此类测试用例表明测试套件已连接到该功能的实现细节。

如果sort函数的实现细节发生变化,可能会改变两个排序算法之间的限制或者使用mergesort或其他任何东西替换heapsort:现有的测试将继续有效。然而,它们的价值是值得怀疑的,并且它们可能需要重新设计以更好地测试改变的排序函数。换句话说,尽管测试是在公共API上进行的,但仍会有维护工作。

B)如何测试实现细节

许多人认为不应该测试私有功能或实现细节的一个原因是,实现细节更有可能发生变化。这种更高的变化可能性至少是隐藏接口背后的实现细节的原因之一。

现在,假设接口后面的实现包含更大的私有部分,内部接口上的单独测试可能是一个选项。有些人争辩说,这些部分不应该在私有时进行测试,它们应该变成公开的东西。一旦公开,单元测试该代码就行了。

这很有趣:虽然界面是内部的,但它很可能会改变,作为一个实现细节。采用相同的界面,将其公开进行一些神奇的转变,即将其转变为不太可能改变的界面。显然这个论证存在一些缺陷。

但是,背后还有一些事实:在测试实现细节时,特别是使用内部接口时,应该努力使用可能保持稳定的接口。然而,某些界面是否可能是稳定的,不仅仅是根据它是公共的还是私人的可判定的。在我已经工作了一段时间的世界项目中,公共接口也经常发生变化,许多私有接口长期保持不变。

尽管如此,使用“前门首先”是一个很好的经验法则(见http://xunitpatterns.com/Principles%20of%20Test%20Automation.html)。但请记住,它被称为“前门首先”而不是“前门”。

C)总结

还测试实现细节。更喜欢在稳定的接口(公共或私有)上进行测试。如果实现细节发生变化,则还需要修改对公共API的测试。把一些私人事物公之于众并不会神奇地改变它的稳定性。

另一答案

是的,你应该尽可能测试私人方法。为什么?为了避免不必要的state space explosion测试用例,最终只是在相同的输入上重复隐式地测试相同的私有函数。让我们用一个例子来解释原因。

考虑以下略有设想的例子。假设我们想要公开一个占用3个整数的函数,并且当且仅当这3个整数都是素数时才返回true。我们可以像这样实现它:

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

现在,如果我们采取严格的方法,只应测试公共函数,我们只允许测试allPrime而不是isPrimeandAll

作为测试者,我们可能对每个论点的五种可能性感兴趣:< 0= 0= 1prime > 1not prime > 1

以上是关于我应该测试私有方法还是仅测试公共方法? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Java单元测试对私有方法测试

Java单元测试对私有方法测试

java中的单元测试私有和静态方法

在 Raku 中测试私有方法

使用 GoogleTest 测试私有方法的最佳方法是啥? [关闭]

如何为调用 API 的方法编写 JUnit 测试?