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

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


当前回答

我最近遇到了这个问题,并编写了一个名为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));

其他回答

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

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

我认为大多数答案都太粗糙了。这取决于您的代码是否应该测试私有方法。

我过去使用了私人方法测试,我是通过反思来完成的。它对我有用。我意识到它的问题,但对我来说,这是最好的解决方案。

我有一个强大的应用程序,可以模拟100多万人口中的人类行为。每个人都由一个对象表示。该应用程序的主要目的是跟踪某些东西(疾病、信息、思想)如何在人群中传播。

为此,我有方法将疾病或信息从一个对象传递给另一个对象。绝对没有理由公开这些方法,因为最终用户对人与人之间的一次传递不感兴趣。最终用户只对它如何在人群中传播的全貌感兴趣。所以这些方法是私人的。

但我想肯定的是,从一个人向另一个人传递一点信息的行为是否在做它应该做的事情。通过公共用户界面测试它也是不可能的,因为它只是不公开,我认为仅仅为了测试而公开它是很尴尬的。应用程序的最终输出由一直执行的数亿个这样的单个步骤定义。我也不能测试最终输出,因为这涉及到复杂的随机性,这使得它无法预测。

因此,我测试应用程序的方法是测试从一个人向另一个人传递信息的单个步骤。这些都是私人方法。所以我用反思来检验这一点。

我讲这个故事是为了表明这不是一个简单的黑白故事。这取决于您的应用程序。在某些情况下,通过反射测试私有方法可能是最好的选择。

如果这里的一些人在我的用例中知道更好的解决方案,我当然乐意接受纠正。。。。

如果您确实需要直接测试私有方法/类等,那么可以使用其他答案中已经提到的反射。然而,如果说到这一点,我宁愿使用框架提供的实用程序类,而不是直接处理反射。例如,对于Java,我们有:

Spring框架中的ReflectionUtilsJUnit中的ReflectionUtils

关于如何使用它们,你可以在网上找到很多文章。我特别喜欢这个:

org.springframework.util.ReflectionUtils的Java代码示例

首先,我要抛出一个问题:为什么你的私人成员需要隔离测试?它们是否如此复杂,提供了如此复杂的行为,以至于需要在公共表面之外进行测试?这是单元测试,而不是“代码行”测试。别为小事操心。

如果它们是那么大,足够大,以至于这些私有成员都是一个复杂度很大的“单元”,那么考虑将这些私有成员从这个类中重构出来。

如果重构不合适或不可行,在进行单元测试时,是否可以使用策略模式来替换对这些私有成员函数/成员类的访问?在单元测试中,该策略将提供额外的验证,但在发布版本中,它将是简单的传递。

对于C++(从C++11开始),将测试类添加为好友非常有效,不会破坏生产封装。

让我们假设我们有一些类Foo和一些真正需要测试的私有函数,还有一些类FooTest应该可以访问Foo的私有成员。然后我们应该写下以下内容:

// prod.h: some production code header

// forward declaration is enough
// we should not include testing headers into production code
class FooTest;

class Foo
{
  // that does not affect Foo's functionality
  // but now we have access to Foo's members from FooTest
  friend FooTest;
public:
  Foo();
private:
  bool veryComplicatedPrivateFuncThatReallyRequiresTesting();
}
// test.cpp: some test
#include <prod.h>

class FooTest
{
public:
  void complicatedFisture() {
    Foo foo;
    ASSERT_TRUE(foo.veryComplicatedPrivateFuncThatReallyRequiresTesting());
  }
}

int main(int /*argc*/, char* argv[])
{
  FooTest test;
  test.complicatedFixture();  // and it really works!
}