主持人注:这里已经有39个答案了(有些已经删除了)。在你发表你的答案之前,考虑一下你是否可以为讨论添加一些有意义的东西。你很可能只是在重复别人已经说过的话。


我偶尔发现自己需要将类中的私有方法设为public,只是为了为它编写一些单元测试。

通常这是因为该方法包含类中其他方法之间共享的逻辑,并且单独测试逻辑更整洁,或者另一个原因可能是我想测试同步线程中使用的逻辑,而不必担心线程问题。

其他人发现他们这样做是因为我不喜欢吗?我个人认为,公开一个方法的好处超过了它在类之外没有提供任何服务的问题……

更新

谢谢大家的回答,似乎引起了大家的兴趣。我认为普遍的共识是测试应该通过公共API进行,因为这是使用类的唯一方式,我非常同意这一点。在我上面提到的几个案例中,我会这样做,这是不常见的情况,我认为这样做的好处是值得的。

然而,我可以看到,每个人都指出它不应该真的发生。再仔细想想,我觉得改变你的代码来适应测试是一个坏主意——毕竟我认为测试在某种程度上是一个支持工具,而改变一个系统来“支持一个支持工具”是明显的坏做法。


当前回答

Guava有一个@VisibleForTesting注释,用于标记那些扩大了作用域(包或公共)的方法。我使用@Private注释来做同样的事情。

虽然必须测试公共API,但有时获取通常不是公共的东西是方便和明智的。

当:

通过将一个类分解为多个类,类的可读性会显著降低, 为了让它更容易测试, 并且提供一些对内部的测试访问就可以做到这一点

宗教似乎战胜了工程学。

其他回答

实际上,有些情况下你应该这样做(例如,当你实现一些复杂的算法时)。只做package-private,这就足够了。 但在大多数情况下,你可能有太复杂的类,这就需要把逻辑分解到其他类中。

单元测试应该测试公共契约,这是在代码的其他部分使用类的唯一方法。私有方法是实现细节,你不应该测试它;只要公共API能够正常工作,实现并不重要,并且可以在不改变测试用例的情况下进行更改。

私有方法通常用作“助手”方法。因此,它们只返回基本值,从不操作对象的特定实例。

如果您想测试它们,您有几个选项。

使用反射 给这些方法包访问权

或者,如果helper方法是一个足够好的新类候选,您可以使用它作为公共方法创建一个新类。

这里有一篇很好的文章。

许多答案建议只测试公共接口,但恕我直言,这是不现实的——如果一个方法执行的操作需要5个步骤,那么您将希望分别测试这5个步骤,而不是一起测试。这需要测试所有五个方法,否则它们(除了测试之外)可能是私有的。

测试“私有”方法的通常方法是给每个类一个自己的接口,并将“私有”方法设为公共的,但不将它们包含在接口中。这样,它们仍然可以被测试,但不会使界面膨胀。

是的,这将导致文件和类膨胀。

是的,这确实使公共和私有说明符变得多余。

是啊,这真让人头疼。

不幸的是,这是我们为使代码可测试而做出的众多牺牲之一。也许未来的语言(或者甚至是c# /Java的未来版本)将具有使类和模块可测试性更加方便的特性;但与此同时,我们得跳过这些障碍。


There are some who would argue that each of those steps should be its own class, but I disagree - if they all share state, there is no reason to create five separate classes where five methods would do. Even worse, this results in file- and class-bloat. Plus, it infects the public API of your module - all those classes must necessarily be public if you want to test them from another module (either that, or include the test code in the same module, which means shipping the test code with your product).

我倾向于认为进行单元测试所带来的好处超过了增加某些成员曝光度所带来的问题。稍微改进一下,将其设置为受保护的虚拟对象,然后在测试类中覆盖它以公开它。

或者,如果你想单独测试它的功能,它是否表明你的设计中缺少一个对象?也许你可以把它放在一个单独的可测试类中……那么你现有的类只是委托给这个新类的一个实例。