题 如何测试私有函数或具有私有方法,字段或内部类的类?


如何对具有内部私有方法,字段或嵌套类的类进行单元测试(使用xUnit)?或者通过拥有私有的功能 内部联系 (static 在C / C ++)或私人(匿名)命名空间?

为了能够运行测试而改变方法或函数的访问修饰符似乎很糟糕。


2268


起源


测试私有方法的最佳方法是不直接测试它 - Surya
检查文章 使用JUnit和SuiteRunner测试私有方法。 - Mouna Cheikhna
我不同意。长期或难以理解的(公共)方法必须重构。愚蠢的是不要测试你得到的小(私人)方法,而不仅仅是公共方法。 - Michael Piefel
不测试任何方法只是因为它的可见性是愚蠢的。甚至单元测试应该是最小的代码片段,如果你只测试公共方法,你现在永远不会确定错误发生在哪里 - 那个方法,或者其他一些方法。 - Dainius
你需要测试这门课 功能而不是它 履行。想测试私人方法吗?测试调用它们的公共方法。如果该类提供的功能经过彻底测试,则其内部结构已经证明是正确可靠的;你不需要测试内部条件。测试应保持与测试类的分离。 - Calimar41


答案:


如果你有一些遗产 Java的 应用程序,并且您不允许更改方法的可见性,测试私有方法的最佳方法是使用 反射

在内部,我们使用帮助器来获取/设置 private 和 private static 变量以及调用 private 和 private static 方法。以下模式将允许您执行与私有方法和字段相关的任何操作。当然你不能改变 private static final 变量通过反思。

Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

对于领域:

Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

笔记:
  1。 targetClass.getDeclaredMethod(methodName, argClasses) 让你看看 private 方法。同样的事情也适用    getDeclaredField
  2. setAccessible(true) 需要和私人一起玩。


1417



如果您不熟悉API,则很有用,但如果您必须以这种方式测试私有方法,那么您的设计就会出现问题。正如另一张海报所说,单元测试应该测试类的合同:如果合同太宽泛并且实例化系统太多,那么应该解决设计问题。 - andygavin
很有用。使用此功能时,请务必记住,如果在混淆后运行测试,则会严重失败。 - Rick Minerich
示例代码对我不起作用,但这使得更清晰: java2s.com/Tutorial/Java/0125__Reflection/... - Rob
比直接使用反射更好的是使用一些库,例如 Powermock。 - Michael Piefel
这就是测试驱动设计有用的原因。它可以帮助您确定需要暴露的内容以验证行为。 - Thorbjørn Ravn Andersen


测试私有方法的最佳方法是通过另一种公共方法。如果无法执行此操作,则满足下列条件之一:

  1. 私有方法是死代码
  2. 您正在测试的课程附近有一种设计气味
  3. 您尝试测试的方法不应该是私有的

507



不同意。在私有方法中使用算法是完全有效的,这需要比通过类的公共接口实现更多的单元测试。 - Mr. Shiny and New 安宇
嘿Shiny先生是的,但是你会有脆弱的考验。另外,请参阅我的回复中的第2个。 - Trumpi
脆弱的测试是一种在代码发生变化时很容易失败的测试。通常,当测试结果基于方法的作用而不是预期的输出和给定一组输入的副作用时,就会发生这种情况。理想情况下,对代码的更改不会改变结果不应该破坏测试。 - aro_biz
@先生。闪亮和新颖:如果你的私有方法足够复杂以保证独立的单元测试,那么它就足够复杂,可以拥有自己的类。该课程当然可以是内部的(即无法从其他包中访问)。 - sleske
@sleske,我不同意。你可以有一个小的私人方法,根据设计你需要它是私人的。我宁愿测试每一小段代码,也不愿通过公共方法测试私有方法。这种测试方式将进行组件测试。 - despot


当我在一个足够复杂的类中有私有方法时,我觉得需要直接测试私有方法,这就是代码味道:我的类太复杂了。

我解决这些问题的常用方法是梳理一个包含有趣位的新类。通常,此方法及其与之交互的字段可能会被提取到新类中。

新类将这些方法公开为“公共”,因此它们可以进行单元测试。新旧课程现在都比原来的课程简单,这对我来说很棒(我需要保持简单,否则我会迷路!)。

请注意,我并不是说人们在不使用大脑的情况下创建课程!这里的要点是使用单元测试的力量来帮助您找到好的新类。


272



问题是如何测试私有方法。你说你会为此做新的课程(并增加更多的复杂性)并建议不要创建新课程。那么如何测试私有方法呢? - Dainius
我认为如果你有方法,就不需要创建另一个类只是为了能够测试该方法。我不认为类设计在这里有问题。所以我假设作者之前做了所有事情以便进行适当的设计,因此为一种方法引入另一个类只会增加复杂性。当然,当你有足够复杂的程序时,就没有明显的错误.. - Dainius
@Dainius:我不建议创建一个新类 独自 所以你可以测试那个方法。我建议编写测试可以帮助您改进设计:好的设计很容易测试。 - Jay Bazuzi
但是你同意好的OOD会暴露(公开)只有那个类/对象正常工作所必需的方法吗?所有其他应该是私人/ protectec。因此在一些私有方法中会有一些逻辑,IMO测试这些方法只会提高软件的质量。当然我同意如果某些代码是复杂的,它应该分成单独的方法/类。 - Dainius
@Danius:在大多数情况下,添加新类会降低复杂性。只是衡量它。在某些情况下,可测试性与OOD作斗争。现代方法有利于可测试性。 - AlexWien


我用过 反射 在过去为Java做这个,在我看来这是一个很大的错误。

严格来说,你应该  编写直接测试私有方法的单元测试。你是什​​么 应该 测试是该类与其他对象的公共合同;你永远不应该直接测试对象的内部。如果另一个开发人员想要对该类进行少量内部更改(这不会影响类公共合同),则他/她必须修改基于反射的测试以确保其有效。如果您在整个项目中反复执行此操作,则单元测试将不再是对代码运行状况的有用度量,并开始成为开发的障碍,并对开发团队造成烦扰。

我建议改为使用Cobertura等代码覆盖工具,以确保您编写的单元测试能够在私有方法中提供良好的代码覆盖率。这样,您可以间接测试私有方法正在做什么,并保持更高的敏捷性。


229



+1给这个。在我看来,这是问题的最佳答案。通过测试私有方法,您正在测试实现。这违背了单元测试的目的,即测试类合同的输入/输出。测试应该 只要 足够了解实现,以模拟它调用其依赖项的方法。而已。如果您无需更改测试就无法更改实施 - 可能是您的测试策略很差。 - Colin M
@Colin M这不是他真正想要的;)让他来决定,你不知道这个项目。 - Aaron Marcus
不是真的。通过使用它的公共方法测试私有方法的一小部分可能需要花费很多精力。在到达调用私有方法的行之前,公共方法可能需要进行大量设置 - ACV


从这篇文章: 使用JUnit和SuiteRunner测试私有方法 (Bill Venners),你基本上有4种选择:

  • 不要测试私有方法。
  • 给方法包访问权限。
  • 使用嵌套测试类。
  • 使用反射。

187



@ user86614将测试代码放入生产代码中并不是一个好主意。 - Jimmy T.
@JimmyT。,取决于“生产代码”的用途。我会调用为驻留在VPN中的应用程序生成的代码,其目标用户是系统管理员,是生产代码。 - Pacerier
@Pacerier你是什么意思? - Jimmy T.
第五个选项,如上所述是测试调用私有方法的公共方法?正确? - user2441441
@LorenzoLerate我一直在努力解决这个问题,因为我正在进行嵌入式开发,我希望我的软件尽可能小。尺寸限制和性能是我能想到的唯一原因。


通常,单元测试旨在运用类或单元的公共接口。因此,私有方法是您不希望显式测试的实现细节。


96



这是IMO的最佳答案,或者通常说的是测试行为,而不是方法。单元测试不是源代码指标,静态代码分析工具和代码审查的替代品。如果私有方法如此复杂以至于需要分离测试,那么它可能需要重构,而不是更多的测试。 - Dan Haynes
所以不要写私有方法,只需创建10个其他小类? - razor


只是我想要测试私有方法的两个例子:

  1. 解密程序 - 我不会 想让任何人都能看到它们 为了测试,别人都可以 用它们来解密。但他们是 代码固有的,复杂的, 并且需要始终工作(明显的例外是反射,在大多数情况下,甚至可以用于查看私有方法,何时 SecurityManager 没有配置来防止这种情况)。
  2. 创建SDK 为了社区 消费。在这里,公众接受了 完全不同的意思,因为这 是全世界都可以看到的代码 (不仅仅是我的应用程序的内部)。我放 代码转换为私有方法,如果我不这样做 希望SDK用户看到它 - 我 不要把它看作代码味道 与SDK编程如何工作。但是 当然我还需要测试一下 私人方法,他们在哪里 实际上我的SDK的功能 住。

我理解只测试“合同”的想法。但是我没有看到人们可以提倡实际上没有测试代码 - 你的里程可能会有所不同。

因此,我的权衡涉及使JUnits与反射复杂化,而不是损害我的安全性和SDK。


56



虽然你永远不应该公开方法来测试它, private 不是唯一的选择。没有访问修饰符 package private 并且意味着只要您的单元测试位于同一个包中,您就可以对其进行单元测试。 - ngreen
@ngreen这一切都是真的,但不是问题所在。 OP询问有关测试私有方法的问题。默认值与private不同;如上所述,只需在同一个包中声明该类,就可以轻松地从类中看到默认访问方法。私人访问,你需要反思,这是一个完整的其他球赛。 - Richard Le Mesurier
我正在评论你的答案,特别是第1点,而不是OP。没有必要将方法设为私有,因为您不希望它是公开的。 - ngreen
@ngreen true thx - 我对“公共”这个词很懒。我更新了答案,包括公共,受保护,默认(以及提及反思)。我试图提出的一点是,有充分的理由让一些代码保密,但这不应该阻止我们对它进行测试。 - Richard Le Mesurier
感谢您为每个人提供真实的例子。大多数答案都很好,但从理论的角度来看。在现实世界中,我们确实需要隐藏合同中的实现,我们仍然需要对其进行测试。阅读所有这些,我想也许这应该是一个全新的测试级别,具有不同的名称 - Z. Khullah


私有方法由公共方法调用,因此公共方法的输入还应测试由这些公共方法调用的私有方法。当公共方法失败时,那可能是私有方法失败。


53



真实的事实。问题是当实现更复杂时,就像一个公共方法调用几个私有方法一样。哪一个失败了?或者,如果它是一个复杂的私有方法,则无法公开或转移到新类 - Z. Khullah
您已经提到过应该测试私有方法的原因。你说“那可能是”,所以你不确定。 IMO,是否应该测试方法与其访问级别正交。 - Wei Qiu