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

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


当前回答

我不确定这是否是一种好的技术,但我开发了以下模式来单元测试私有方法:

我不修改要测试的方法的可见性,也不添加其他方法。相反,我为要测试的每个私有方法添加了一个额外的公共方法。我调用这个额外的方法TestPort,并用前缀t_表示它们。然后,此测试端口方法只需访问相应的私有方法。

此外,我向测试端口方法添加了一个布尔标志,以决定是否从外部通过测试端口方法授予对私有方法的访问权。然后在静态类中全局设置该标志,在该类中我放置了例如应用程序的其他全局设置。因此,我可以在一个地方打开和关闭对私有方法的访问,例如,在相应的单元测试中。

其他回答

JML具有spec_public注释注释语法,允许您在测试期间将方法指定为public:

private /*@ spec_public @*/ int methodName(){
...
}

2.4隐私修改器和可见性中讨论了此语法。还有一个将JML规范转换为JUnit测试的程序。我不确定它的工作情况如何,或者它的功能是什么,但似乎没有必要,因为JML本身就是一个可行的测试框架。

测试私有方法的最佳方法是通过另一个公共方法。如果无法做到这一点,则以下条件之一为真:

私有方法是死代码您正在测试的类附近有设计气味您尝试测试的方法不应是私有的

如果使用Spring,ReflectionTestUtils提供了一些方便的工具,可以帮助您以最小的工作量完成任务。例如,要在私有成员上设置模拟,而不必强制添加不需要的公共setter:

ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);

在使用ExpectedException时,对Cem Catikka的答案进行了快速补充:

请记住,预期的异常将被包装在InvocationTargetException中,因此为了获得异常,必须抛出收到的InvocationTargetException的原因。类似于(在BizService上测试私有方法validateRequest()):

@Rule
public ExpectedException thrown = ExpectedException.none();

@Autowired(required = true)
private BizService svc;


@Test
public void testValidateRequest() throws Exception {

    thrown.expect(BizException.class);
    thrown.expectMessage(expectMessage);

    BizRequest request = /* Mock it, read from source - file, etc. */;
    validateRequest(request);
}

private void validateRequest(BizRequest request) throws Exception {
    Method method = svc.getClass().getDeclaredMethod("validateRequest", BizRequest.class);
    method.setAccessible(true);
    try {
        method.invoke(svc, request);
    }
    catch (InvocationTargetException e) {
        throw ((BizException)e.getCause());
    }
 }

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