108

JUnit 只会测试我的类中那些公开的方法。我如何对那些不受保护的(即私有的、受保护的)进行junit测试?

我可以不使用 junit 来测试它们,但我想知道 junit 标准方法是什么。

4

17 回答 17

94

一种关于单元测试的思想流派说,您应该只能测试公共方法,因为您应该只对公共 API 进行单元测试,并且通过这样做,您应该覆盖非公共方法中的代码。你的旅费可能会改变; 我发现有时是这样,有时不是。

话虽如此,有几种方法可以测试非公共方法:

  • 您可以通过将单元测试与它们正在测试的类放在同一个包中来测试受保护的和包范围的方法。这是一种相当普遍的做法。
  • 您可以通过创建被测类的子类来测试另一个包中的单元测试中的受保护方法,该子类将您要测试的方法覆盖为公共方法,并让这些被覆盖的方法使用 super 关键字调用原始方法。通常,这个“测试子类”将是执行测试的 JUnit TestCase 类中的一个内部类。在我看来,这有点 hacky,但我已经做到了。

希望这可以帮助。

于 2009-01-13T21:09:28.527 回答
40

与许多单元测试问题一样,测试私有方法实际上是变相的设计问题。与其尝试做任何棘手的事情来测试私有方法,当我发现自己希望为私有方法编写测试时,我会花一点时间问自己,“我需要如何设计它,以便我可以通过公共方法彻底测试它?”

如果这不起作用,JUnitX 允许测试私有方法,尽管我相信它仅适用于 JUnit 3.8。

于 2009-01-13T21:09:32.910 回答
26

当您编写 JUnit 测试时,您必须做一个微妙的思维转变:“我现在是我自己班级的客户。” 这意味着私有是私有的,您只测试客户端看到的行为。

如果该方法真的应该是私有的,我会认为它是一个设计缺陷,只是为了测试而使其可见。您必须能够根据客户看到的情况推断其正确操作。

自从我最初写这篇文章以来的三年里,我开始使用 Java 反射稍微不同地处理这个问题。

肮脏的小秘密是,您可以使用反射在 JUnit 中测试私有方法,就像测试公共方法一样。您可以随心所欲地进行测试,但仍然不会将它们公开给客户。

于 2009-01-13T21:42:20.083 回答
9

最简单的解决方案是将 JUnit 测试放在同一个包(但不同的目录)中,并对方法使用默认(即包私有)可见性。

另一种更复杂的方法是使用反射来访问私有方法。

于 2009-01-13T21:07:17.983 回答
9

如果您在相对较少的“公共”入口点下隐藏了大量逻辑,那么您可能违反了单一职责原则。如果可能,您将希望将代码重构为多个类,最终导致更多的“公共”方法可供测试。

于 2009-01-13T21:12:05.993 回答
8

这是其他人一直在抱怨你的“可能不应该这样做”的方法。不过,我认为这样做肯定是有理由的。以下代码将访问私有字段,但私有方法的代码几乎相同。

public void testPrivateField() throws InterruptedException {
    Class<ClassWPrivateField> clazz = ClassWPrivateField.class;
    try {
        Field privateField = clazz.getDeclaredField("nameOfPrivateField");
        privateField.setAccessible(true); // This is the line
        // do stuff
    } catch(NoSuchFieldException nsfe) {
        nsfe.printStackTrace();
        fail();
    } catch(IllegalAccessException iae) {
        iae.printStackTrace();
        fail();
    }

}
于 2009-01-13T22:22:56.833 回答
3

我几乎总是在我的 Java 项目中使用 Spring,因此,我的对象是为依赖注入而构建的。它们往往是在应用程序上下文中组装的公共接口的相当精细的实现。因此,我很少(如果有的话)需要测试私有方法,因为类本身足够小,根本不是问题。

即使我不使用 Spring,我也倾向于采用相同的做法,将小而简单的对象组装成越来越大的抽象,每个抽象都相对简单,但由于聚合对象而变得复杂。

根据我的经验,需要对私有方法进行单元测试表明您正在测试的内容可以(并且应该)被简化。

也就是说,如果你仍然真的觉得有必要:

  • 受保护的方法可以通过子类进行测试;
  • 包私有方法可以通过将单元测试放在同一个包中进行测试;和
  • 私有方法可以通过提供例如包私有工厂代理方法进行单元测试。不理想,但私人确实意味着私人。
于 2009-01-13T21:31:16.873 回答
3

您通常不测试私有方法,因为它们只能(通常)通过另一个公共方法间接测试。当您测试驾驶并制作私有方法时,它们通常是“提取方法”重构的结果,并且已经间接测试过。

如果您担心使用大量逻辑测试私有方法,那么您可以做的最聪明的事情就是将该代码移动到公共方法中的另一个类中。完成此操作后,使用此代码的先前方法可以通过存根或模拟提供的功能来简化测试。

于 2009-01-13T21:41:05.077 回答
3

我遇到了同样的问题,并且“如果它需要私有,它可能应该被重构”并不适合我。

假设您有某种功能要以某种方式在类内部分离出来。例如,假设我有这样的事情:

public class HolderOfSomeStrings{

    private List<String> internal_values;

    public List<String> get()
    {
       List<String> out = new ArrayList<String>();
       for (String s:internal_values)
       { 
           out.add(process(s));
       }
      return get;
    }

   private static String process(String input)
   {
     //do something complicated here that other classes shouldn't be interested in
    }

}

这里的重点是 junit 迫使我公开进程,或者至少是受保护的,或者把它放在它自己的实用程序类中。但是,如果它是 HolderOfSomeStrings 的某种内部逻辑,我根本不清楚这是正确的——在我看来,这应该是私有的,并且以某种方式使其更明显会混淆代码。

于 2009-01-13T22:18:16.687 回答
2

借用 Andy Hunt 的想法,即使您的私有方法也必须有一些您感兴趣的副作用。换句话说,它们必须从某个公共方法调用并执行一个有趣的任务,从而导致您的对象状态发生变化. 测试该状态变化。

假设您有公共方法 pubMethod 和私有方法 privMethod。当您调用 pubMethod 时,它会依次调用 privMethod 来执行任务(可能解析字符串)。pubMethod 然后使用这个解析的字符串以某种方式设置成员变量的值或影响它自己的返回值。通过观察对 pubMethod 的返回值或成员变量(可能通过使用访问器获取它们)的预期效果进行测试。

于 2009-01-14T06:12:35.950 回答
2

DP4j 罐子

为了测试私有方法,我们需要使用反射及其在所有答案中的指向。

现在这个任务在 Dp4j jar 的帮助下得到了简化。

  • Dp4j 分析您的代码并自动为您生成反射 API 代码。

  • 只需将 dp4j.jar 添加到您的 CLASSPATH。

  • Dp4j.jar 包含注释处理器,它们将在您的代码中查找使用 @Test JUnit 注释进行注释的方法。

  • dp4j 分析这些方法的代码,如果发现您非法访问私有方法,它将用使用 Java 反射 API 的等效代码替换您无效的私有方法引用。

在此处获取更多详细信息

于 2015-08-26T06:18:27.600 回答
1

您可以使用 TestNG 而不是 JUnit,它不关心方法是私有的还是公共的。

于 2010-08-18T14:41:33.017 回答
1

如上所述使用反射来测试私有方法。如果我们遵循 TDD,我们应该测试私有方法,因为 TDD 意味着以后不会有意外。所以一个人不应该等待完成他的公共方法来测试私人。这有助于在重构时进行更细粒度的回归测试。

于 2012-04-11T08:08:36.133 回答
0

寻找“PrivateAccessor.invoke”。我的代码从“junitx.util”导入它,但我不知道它来自哪里。

于 2009-01-13T21:05:41.077 回答
0

几乎同意每一篇文章——你可能应该重构并且可能不测试私有,除非通过公共,只是想添加一种不同的方式来思考它......

将您的类本身视为“单元”,而不是方法。您正在测试该类,并且无论如何调用它的公共方法,它都可以保持有效状态。

调用私有方法会破坏封装并实际上使测试无效。

于 2009-01-13T22:13:45.883 回答
0

作为这种情况的一种分支,我不确定每个人都对整个“多语言编程”问题的看法,但是 Groovy 测试可以在 Junit 中运行,并且忽略整个公共/非公共问题。旁注,它被官方归类为“错误”,但当他们试图修复它时,掀起了一场风暴,以至于它被放回了原来的样子。

于 2010-09-03T20:11:28.840 回答
0

我完全同意@duffymo 的观点,即应该从客户的角度来测试代码(尽管他说他不再这么想了)。但是,从这个角度来看,private vs others 有不同的含义。私有方法的客户端是类本身,所以我更喜欢通过外部(公共/包保护)API 测试它们。但是,受保护的和受包保护的成员是为外部客户端而存在的,所以我用继承自有类或驻留在同一个包中的假货来测试它们。

于 2017-05-28T00:57:48.047 回答