Junit测试私有方法

时间:2013-02-24 17:35:22

标签: java reflection junit

我正在尝试编写一个实用程序方法,以便在被测试的类中轻松调用私有方法。 我有这个:

private Object callPrivateMethod(String methodName, Object subject, Object... parameters) {

    try {
        Class<?>[] paramTypes = new Class<?>[parameters.length];
        for (int index=0; index<parameters.length; index++) {
            paramTypes[index] = parameters[index].getClass();
        }
        Method method = subject.getClass().getDeclaredMethod(methodName, paramTypes);
        method.setAccessible(true);
        return method.invoke(subject, parameters);
    } catch (Exception e) {
        e.printStackTrace();
        fail(e.getMessage());
        return null;
    }
}

但是当我尝试使用此代码调用它时:

List<Session> sessions = new ArrayList<Session>();
// fill the array list
String sessionLines = (String) callPrivateMethod("getSessionsForEmail", emailSender, sessions);

我得到了这个例外:

java.lang.NoSuchMethodException: staffing.server.email.EmailSender.getSessionsForEmail(java.util.ArrayList)

EmailSender(测试中的类)中的方法签名如下所示:

private String getSessionsForEmail(List<Session> sessions) {
   //do stuff
}

试图弄清楚为什么反射找不到方法。与List和ArrayList不完全相同的类是什么?如果是这样,我该怎么办?

5 个答案:

答案 0 :(得分:6)

最佳做法是不直接测试私有方法。通过公共和/或受保护的方法访问它们,我们正在测试系统在生产中的表现。此方法还允许您通过公共/受保护方法发送所有数据组合来管理测试覆盖率。

可以通过在测试源文件夹中使用相同的包名来测试受保护的方法。

如果必须测试私有方法,那么您可以使用Powermock等模拟工具,而不是自己进行反射,最好使用Mockito。这两个工具都与JUnit有很好的集成。这是一个陡峭的学习曲线,但值得投资。以下是更多详细信息:Testing Private method using mockito

答案 1 :(得分:4)

您的代码尝试查找以ArrayList(具体类sessions)为参数的方法。但是您的方法不会将ArrayList作为参数。它需要List作为参数。

除了参数值之外,您还需要传递方法参数的类型。

或者您可以重构代码并使方法受保护或受包保护,或者将其作为公共方法放在另一个协作类中。

答案 2 :(得分:0)

目前的推理说你不应该测试私有方法。它们是特定于实现的,应该通过调用它们的方法进行测试。或者他们应该被提取到他们自己的班级并通过它进行测试。我们的想法是,如果你有私人方法做得太多,他们需要单独测试,你的课程做得太多,应该分手。

以这种方式看待它有很多好处,而不是至少逃避不得不攻击你的班级来测试它。

答案 3 :(得分:0)

如果您真的希望通过自己的实现取得成功,我觉得查看可能有用:Type erasure in Java

否则,正如Akber Choudhry指出的那样,Mockito附带了一个很好的实用程序包,名为org.powermock.reflect.Whitebox。我肯定会用它。

最后,我不会说永远不会测试私有方法,但如果你打算测试它们,请三思而后行。保持基于反射的方法并不是最简单的方法,而且非常容易出错。

答案 4 :(得分:0)

这可以通过 JUnit4 + Mockito 完成。

要模拟私有方法,我们可以简单地使用mockit.Deencapsulation包:

Deencapsulation.invoke(myService, "methodName", arg1, arg2);

我们可以根据要求用Mockito.anyString()等替换参数。