junit& java:测试非公共方法

时间:2009-01-13 21:03:25

标签: java unit-testing testing junit

JUnit只会在我的班级中测试那些公开的方法。如何对非(即私有,受保护)的进行junit测试?

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

17 个答案:

答案 0 :(得分:87)

关于单元测试的一个思想学派说,你应该只能测试公共方法,因为你应该只对你的公共API进行单元测试,这样做,你应该覆盖非你的代码公共方法。你的旅费可能会改变;我发现有时候这种情况有时并非如此。

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

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

希望这有帮助。

答案 1 :(得分:36)

与许多单元测试问题一样,测试私有方法实际上是伪装的设计问题。当我发现自己希望为私人方法编写测试时,我花了一分钟时间问自己,“我怎么需要设计这个以便我可以通过公共方法彻底测试它?”而不是尝试做任何棘手的测试私有方法。

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

答案 2 :(得分:25)

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

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

自从我最初写这篇文章以来的三年里,我已经开始使用Java反射稍微改变问题。

肮脏的小秘密是,您可以使用反射在JUnit中测试私有方法,就像使用公共方法一样。您可以根据自己的内容进行测试,但仍然不会将其公开给客户。

答案 3 :(得分:9)

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

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

答案 4 :(得分:9)

如果你在相对较少的“公共”入口点埋藏了大量逻辑,那么你可能违反了Single Responsibility Principle。如果可能,您需要将代码重构为多个类,最终导致更多“公共”方法进行测试。

答案 5 :(得分: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();
    }

}

答案 6 :(得分:3)

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

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

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

那就是,如果你真的觉得有必要:

  • 受保护的方法可以通过子类进行测试;
  • 可以通过将单元测试放在同一个包中来测试包私有方法;和
  • 可以通过提供例如包私有工厂代理方法来对私有方法进行单元测试。不理想,但私人确实意味着私人。

答案 7 :(得分:3)

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

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

答案 8 :(得分:2)

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

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

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的某种内部逻辑,那对我来说并不清楚这是正确的 - 在我看来,这应该是私有的,并且在某种程度上使代码变得更加可见。

答案 9 :(得分:2)

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

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

答案 10 :(得分:2)

  

DP4j Jar

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

现在,在Dp4j jar的帮助下简化了这项任务。

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

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

  • Dp4j.jar包含Annotation Processors,它们将在代码中查找使用@Test JUnit注释注释的方法。

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

获取更多详情here

答案 11 :(得分:1)

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

答案 12 :(得分:1)

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

答案 13 :(得分:0)

我绝对同意@duffymo应该从客户的角度测试代码(尽管他说他不会这样思考)。但是,从这个角度来看,私人与其他人有不同的含义。私有方法的客户端本身就是类,所以我更喜欢通过外部(公共/包保护)API来测试它们。但是,受保护和受包保护的成员可以用于外部客户端,因此我使用伪造方法对其进行测试,这些伪造方式继承了拥有类或者驻留在同一个包中。

答案 14 :(得分:0)

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

答案 15 :(得分:0)

作为这种分支的一种分支,我不确定每个人都会在整个“多语言编程”问题上出现问题,但是Groovy测试在Junit中是可以运行的,而忽略了整个公共/非公共问题。侧面说明,它被正式归类为“虫子”,但是当他们试图修复它时,就会发生这样的风暴,它被放回原来的状态。

答案 16 :(得分:0)

与几乎所有帖子一致 - 你应该重构并且可能不会通过公开测试私有,只是想添加一种不同的方式来思考它...

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

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