2

我正在尝试编写一个实用方法来允许在被测类中轻松调用私有方法。我所拥有的是:

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 不是完全相同的类有关吗?如果是这样,我该怎么办?

4

5 回答 5

6

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

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

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

于 2013-02-24T17:41:41.110 回答
4

您的代码试图找到一个将ArrayList(的具体类sessions)作为参数的方法。但是您的方法不接受ArrayListas 参数。它需要一个Listas 参数。

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

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

于 2013-02-24T17:43:31.400 回答
0

当前的推理表明您不应该测试私有方法。它们要么是特定于实现的,并且应该通过调用它们的方法进行测试。或者应该将它们提取到自己的类中并通过它进行测试。想法是,如果你有很多私有方法需要单独测试,那么你的类做的太多,应该被分解。

以这种方式看待它有很多好处,最重要的是避免不得不破解你的类来测试它。

于 2013-02-24T17:48:32.387 回答
0

如果您真的想通过自己的实现取得成功,我觉得看看:Java 中的类型擦除可能会很有用。

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

最后,我不会说永远不要测试私有方法,但如果你打算测试它们,请三思。维护基于反射的方法并不是最简单的事情,而且很容易出错。

于 2013-02-24T18:11:16.803 回答
0

这可以通过JUnit4 + Mockito来完成。

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

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

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

于 2016-10-28T06:22:39.693 回答