如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?

仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。


当前回答

如果您想测试不能更改代码的遗留应用程序的私有方法,Java的一个选项是jMockit,它将允许您创建对象的模拟,即使它们对类是私有的。

其他回答

要用大型而古怪的类测试遗留代码,能够测试我现在正在编写的一个私有(或公共)方法通常非常有用。

我在Java中使用junitx.util.PrivateAccessor包。它有很多用于访问私有方法和私有字段的有用的一行程序。

import junitx.util.PrivateAccessor;

PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);

PowerMock.Whitebox是我见过的最好的选项,但当我阅读它的源代码时,它会读取带有反射的私有字段,所以我想我有了答案:

使用PowerMock测试私有内部状态(字段),或仅进行反射,而无需引入其他独立性的开销对于私人方法:事实上,这个问题本身的赞成票,以及大量的评论和答案,表明这是一个非常并发和有争议的话题,无法给出适合每种情况的确切答案。我知道只有合同需要测试,但我们也需要考虑保险范围。事实上,我怀疑只有测试合约才能100%让一个类免于错误。私有方法是那些在定义数据的类中处理数据的方法,因此对其他类不感兴趣,因此我们不能简单地公开以使其可测试。我会尽量不去测试它们,但当你必须的时候,就去尝试,忘记这里的所有答案。你比互联网上的任何人都更了解自己的处境和限制。当您可以控制代码时,请使用它。经过考虑,但不要过度思考。


一段时间后,当我重新考虑时,我仍然相信这是真的,但我看到了更好的方法。

首先,Powermock.Whitebox仍然可用。

而且,Mockito Whitebox在v2之后被隐藏了(我可以在Whitebox中找到的最新版本是testImplementation“org.Mockito:Mockito core:1.10.19”),并且它一直是org.mockit.internal包的一部分,未来很可能会发生重大变化(请参阅本文)。所以现在我倾向于不使用它。

在Gradle/MMaven项目中,如果您定义了私有方法或字段,那么除了反射之外,没有任何其他方法可以访问它们,因此第一部分是正确的。但是,如果您将可见性更改为“包私有”,则测试包中遵循相同结构的测试将可以访问它们。这也是我们被鼓励在主包和测试包中创建相同层次结构的另一个重要原因。因此,当您可以控制生产代码和测试时,删除私有访问修饰符可能是您的最佳选择,因为相对而言,它不会造成巨大影响。这使得测试和私人方法间谍成为可能。

@Autowired
private SomeService service; // With a package private method "doSomething()"

@Test
void shouldReturnTrueDoSomething() {
    assertThat(doSomething(input), is(true)); // Package private method testing
}

@Test
void shouldReturnTrueWhenServiceThrowsException() {
    SomeService spy = Mockito.spy(service); // Spying real object
    doThrow(new AppException()).when(spy).doSomething(input); // Spy package private method
    ...

}

谈到内部字段,在Spring中有ReflectionUtils.setField()。

最后,有时我们可以绕过问题本身:如果需要满足覆盖要求,也许可以将这些私有方法移到内部静态类中,并忽略Jacobo中的这个类。我只是找到了一些方法来忽略Jacobo Gradle任务中的内部类。另一个问题。

JUnit.org常见问题解答页面的答案:

但如果你必须。。。如果您使用的是JDK1.3或更高版本,则可以使用反射来颠覆访问控制机制。有关如何使用它的详细信息,请阅读本文。如果您使用的是JDK1.6或更高版本,并且使用@测试时,可以使用Dp4j在测试方法中注入反射。对于有关如何使用它的详细信息,请参阅此测试脚本。

P.S.我是Dp4j的主要贡献者。问我你是否需要帮助。:)

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

在内部,我们使用助手来获取/设置私有和私有静态变量,以及调用私有和私有的静态方法。以下模式将允许您执行与私有方法和字段相关的任何操作。当然,您不能通过反射来更改私有静态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);

笔记:TargetClass.getDeclaredMethod(methodName,argClasses)允许您查看私有方法。同样的事情也适用于getDeclaredField。需要setAccessible(true)才能与private一起玩。

私有方法只能在同一类中访问。因此,无法从任何测试类测试目标类的“私有”方法。一种方法是,您可以手动执行单元测试,也可以将方法从“私有”更改为“受保护”。

然后,受保护的方法只能在定义类的同一个包中访问。因此,测试目标类的受保护方法意味着我们需要在与目标类相同的包中定义测试类。

如果以上所有内容都不符合您的要求,请使用反射方式访问私有方法。