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

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


当前回答

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

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

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

其他回答

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

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

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

私有方法被公共方法使用。否则,它们就是死代码。这就是为什么要测试公共方法,断言公共方法的预期结果,从而断言它使用的私有方法。

在公共方法上运行单元测试之前,应该通过调试来测试私有方法。

也可以使用测试驱动开发来调试它们,调试单元测试,直到满足所有断言。

我个人认为最好使用TDD创建类;创建公共方法存根,然后使用预先定义的所有断言生成单元测试,这样在编写代码之前就可以确定方法的预期结果。这样,就不会走上使单元测试断言符合结果的错误道路。然后,当所有单元测试通过时,您的类是健壮的并满足要求。

我最近遇到了这个问题,并编写了一个名为Picklock的小工具,它避免了显式使用Java反射API的问题,两个示例:

通过Java反射调用方法,例如private void方法(String s)

Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");

通过Picklock调用方法,例如private void方法(String s)

interface Accessible {
  void method(String s);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");

设置字段,例如私有BigInteger金额;-通过Java反射

Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));

设置字段,例如私有BigInteger金额;-由Picklock提供

interface Accessible {
  void setAmount(BigInteger amount);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));

如果您试图测试您不愿意或无法更改的现有代码,则反射是一个不错的选择。

如果类的设计仍然是灵活的,并且您有一个复杂的私有方法需要单独测试,我建议您将其导出到一个单独的类中,并单独测试该类。这不需要更改原始类的公共接口;它可以在内部创建helper类的实例并调用helper方法。

如果您想测试来自helper方法的困难错误条件,可以更进一步。从helper类中提取一个接口,向原始类中添加一个公共getter和setter以注入helper类(通过其接口使用),然后将helper类的模拟版本注入原始类中,以测试原始类如何响应helper的异常。如果您想测试原始类而不同时测试助手类,这种方法也很有用。

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

解密例程-我不会想让任何人都能看到它们为了测试,否则任何人都可以使用它们来解密。但他们是代码固有的、复杂的,并且需要始终工作(明显的例外是反射,在大多数情况下,当SecurityManager未配置为防止这种情况发生时,反射甚至可以用于查看私有方法)。为社区创建SDK消耗在这里,公众对完全不同的含义,因为是全世界都能看到的代码(不仅仅是我的应用程序内部)。我把如果我不这样做,请将代码转换为私有方法希望SDK用户看到它-我不要将其视为代码气味SDK编程是如何工作的。但是当然我还需要测试私有方法,它们在哪里我的SDK的功能生活。

我理解只测试“合同”的想法。但我看不出有人可以主张不测试代码,因为您的里程可能会有所不同。

因此,我的权衡涉及到用反射使JUnit测试复杂化,而不是损害我的安全性和SDK。