我已经阅读了这篇关于如何测试私有方法的文章。我通常不测试它们,因为我总是认为只测试将从对象外部调用的公共方法更快。你测试私有方法吗?我应该一直测试它们吗?
当前回答
我认为单元测试是用来测试公共方法的。您的公共方法使用您的私有方法,因此它们也间接地接受测试。
其他回答
它不仅涉及公共或私有方法或函数,还涉及实现细节。私有函数只是实现细节的一个方面。
毕竟,单元测试是一种白盒测试方法。例如,无论谁使用覆盖率分析来识别到目前为止在测试中被忽略的代码部分,都将进入实现细节。
A)是的,你应该测试实现细节:
考虑一个排序函数,如果有多达10个元素,则出于性能考虑使用BubbleSort的私有实现,如果有超过10个元素,则使用不同排序方法(例如堆排序)的私有实现。公共API是排序函数的API。但是,您的测试套件更好地利用了实际上使用了两种排序算法的知识。
在本例中,当然可以在公共API上执行测试。然而,这需要有大量的测试用例来执行具有10个以上元素的排序函数,以便对堆排序算法进行充分的测试。这种测试用例的单独存在表明测试套件连接到功能的实现细节。
如果排序函数的实现细节发生了变化,可能是两种排序算法之间的限制发生了变化,或者堆排序被归并排序取代了,或者其他:现有的测试将继续工作。然而,它们的价值是值得怀疑的,它们可能需要重新工作,以更好地测试更改后的排序函数。换句话说,尽管测试是在公共API上进行的,但仍然需要进行维护工作。
B)如何测试实现细节
许多人认为不应该测试私有函数或实现细节的一个原因是,实现细节更有可能改变。这种更高的更改可能性至少是将实现细节隐藏在接口后面的原因之一。
现在,假设接口背后的实现包含更大的私有部分,可以选择在内部接口上进行单独的测试。有些人认为,这些部分不应该在私人的时候进行测试,它们应该变成公共的东西。一旦公开,对代码进行单元测试就可以了。
这很有趣:虽然接口是内部的,但它很可能会改变,这是一个实现细节。使用相同的接口,将其公开可以进行一些神奇的转换,即将其转换为不太可能更改的接口。显然,这种论证有一些缺陷。
但是,这背后仍然有一些事实:在测试实现细节时,特别是使用内部接口时,应该尽量使用可能保持稳定的接口。然而,某些接口是否可能是稳定的,不能简单地根据它是公共的还是私有的来确定。在我工作过一段时间的项目中,公共接口也经常发生变化,而许多私有接口多年未动。
不过,“先从正门走”是一个很好的经验法则(见http://xunitpatterns.com/Principles%20of%20Test%20Automation.html)。但请记住,这是“前门优先”,而不是“只有前门”。
C)总结
还要测试实现细节。更喜欢在稳定接口(公共或私有)上进行测试。如果实现细节发生变化,也需要修改对公共API的测试。把私人的东西变成公共的东西并不能神奇地改变它的稳定性。
如果方法足够重要/复杂,我通常会让它“受保护”并测试它。一些方法将保持私有,并作为公共/受保护方法的单元测试的一部分进行隐式测试。
I understand the point of view where private methods are considered as implementations details and then don't have to be tested. And I would stick with this rule if we had to develop outside of the object only. But us, are we some kind of restricted developers who are developing only outside of objects, calling only their public methods? Or are we actually also developing that object? As we are not bound to program outside objects, we will probably have to call those private methods into new public ones we are developing. Wouldn't it be great to know that the private method resist against all odds?
我知道有些人会回答说,如果我们正在开发另一个公共方法到那个对象中,那么这个方法应该被测试,就是这样(私有方法可以在没有测试的情况下继续存在)。但这也适用于一个对象的任何公共方法:当开发一个web应用程序时,一个对象的所有公共方法都是从控制器方法调用的,因此可以认为是控制器的实现细节。
那么为什么我们要单元测试对象呢?因为这是非常困难的,不是说不可能,以确保我们正在测试控制器的方法与适当的输入将触发底层代码的所有分支。换句话说,我们在堆栈中的位置越高,测试所有行为就越困难。私有方法也是如此。
对我来说,私人方法和公共方法之间的界限是测试时的心理标准。对我来说更重要的标准是:
该方法是否在不同的地方被多次调用? 这种方法是否复杂到需要测试?
你不应该这么做。如果您的私有方法有足够的复杂性,必须进行测试,您应该将它们放在另一个类中。保持高内聚性,类应该只有一个目的。类公共接口应该足够了。
对于从测试中调用什么api,公共和私有并不是一个有用的区分,方法和类也不是。大多数可测试单元在一个上下文中是可见的,但在其他上下文中是隐藏的。
重要的是覆盖范围和成本。您需要最小化成本,同时实现项目的覆盖目标(行、分支、路径、块、方法、类、等价类、用例……)不管团队怎么决定)。
因此,使用工具来确保覆盖率,并设计您的测试以减少成本(短期和长期)。
不要让测试变得过于昂贵。 如果只测试公共入口是最便宜的,那就这样做。 如果测试私有方法成本最低,那就这么做。
随着您的经验越来越丰富,您将能够更好地预测何时值得重构以避免测试维护的长期成本。