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


当前回答

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

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

A)是的,你应该测试实现细节:

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

在本例中,当然可以在公共API上执行测试。然而,这需要有大量的测试用例来执行具有10个以上元素的排序函数,以便对堆排序算法进行充分的测试。这种测试用例的单独存在表明测试套件连接到功能的实现细节。

如果排序函数的实现细节发生了变化,可能是两种排序算法之间的限制发生了变化,或者堆排序被归并排序取代了,或者其他:现有的测试将继续工作。然而,它们的价值是值得怀疑的,它们可能需要重新工作,以更好地测试更改后的排序函数。换句话说,尽管测试是在公共API上进行的,但仍然需要进行维护工作。

B)如何测试实现细节

许多人认为不应该测试私有函数或实现细节的一个原因是,实现细节更有可能改变。这种更高的更改可能性至少是将实现细节隐藏在接口后面的原因之一。

现在,假设接口背后的实现包含更大的私有部分,可以选择在内部接口上进行单独的测试。有些人认为,这些部分不应该在私人的时候进行测试,它们应该变成公共的东西。一旦公开,对代码进行单元测试就可以了。

这很有趣:虽然接口是内部的,但它很可能会改变,这是一个实现细节。使用相同的接口,将其公开可以进行一些神奇的转换,即将其转换为不太可能更改的接口。显然,这种论证有一些缺陷。

但是,这背后仍然有一些事实:在测试实现细节时,特别是使用内部接口时,应该尽量使用可能保持稳定的接口。然而,某些接口是否可能是稳定的,不能简单地根据它是公共的还是私有的来确定。在我工作过一段时间的项目中,公共接口也经常发生变化,而许多私有接口多年未动。

不过,“先从正门走”是一个很好的经验法则(见http://xunitpatterns.com/Principles%20of%20Test%20Automation.html)。但请记住,这是“前门优先”,而不是“只有前门”。

C)总结

还要测试实现细节。更喜欢在稳定接口(公共或私有)上进行测试。如果实现细节发生变化,也需要修改对公共API的测试。把私人的东西变成公共的东西并不能神奇地改变它的稳定性。

其他回答

如果我发现私有方法非常庞大、复杂或重要到需要自己的测试,我就把它放在另一个类中,并在那里将它设为公共(方法对象)。然后,我可以轻松地测试以前私有但现在是公共的方法,它现在存在于自己的类中。

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

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

A)是的,你应该测试实现细节:

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

在本例中,当然可以在公共API上执行测试。然而,这需要有大量的测试用例来执行具有10个以上元素的排序函数,以便对堆排序算法进行充分的测试。这种测试用例的单独存在表明测试套件连接到功能的实现细节。

如果排序函数的实现细节发生了变化,可能是两种排序算法之间的限制发生了变化,或者堆排序被归并排序取代了,或者其他:现有的测试将继续工作。然而,它们的价值是值得怀疑的,它们可能需要重新工作,以更好地测试更改后的排序函数。换句话说,尽管测试是在公共API上进行的,但仍然需要进行维护工作。

B)如何测试实现细节

许多人认为不应该测试私有函数或实现细节的一个原因是,实现细节更有可能改变。这种更高的更改可能性至少是将实现细节隐藏在接口后面的原因之一。

现在,假设接口背后的实现包含更大的私有部分,可以选择在内部接口上进行单独的测试。有些人认为,这些部分不应该在私人的时候进行测试,它们应该变成公共的东西。一旦公开,对代码进行单元测试就可以了。

这很有趣:虽然接口是内部的,但它很可能会改变,这是一个实现细节。使用相同的接口,将其公开可以进行一些神奇的转换,即将其转换为不太可能更改的接口。显然,这种论证有一些缺陷。

但是,这背后仍然有一些事实:在测试实现细节时,特别是使用内部接口时,应该尽量使用可能保持稳定的接口。然而,某些接口是否可能是稳定的,不能简单地根据它是公共的还是私有的来确定。在我工作过一段时间的项目中,公共接口也经常发生变化,而许多私有接口多年未动。

不过,“先从正门走”是一个很好的经验法则(见http://xunitpatterns.com/Principles%20of%20Test%20Automation.html)。但请记住,这是“前门优先”,而不是“只有前门”。

C)总结

还要测试实现细节。更喜欢在稳定接口(公共或私有)上进行测试。如果实现细节发生变化,也需要修改对公共API的测试。把私人的东西变成公共的东西并不能神奇地改变它的稳定性。

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

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

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

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

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

正如上面引用的,“如果你不测试你的私有方法,你怎么知道它们不会崩溃?”

这是一个大问题。单元测试的一个重要要点是尽快知道在哪里、何时以及如何发生故障。这样就减少了大量的开发和QA工作。如果所有测试的都是公开的,那么您就没有对类的内部进行诚实的覆盖和描述。

我发现最好的方法之一是将测试引用添加到项目中,并将测试放在与私有方法并行的类中。放入适当的构建逻辑,这样测试就不会构建到最终项目中。

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

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

不,你不应该测试私有方法,为什么?此外,流行的mock框架(如Mockito)不支持测试私有方法。