2009-01-13 87 views
80

JUnit只会测试我们课程中公开的那些方法。我如何对那些不是(即私人,受保护)的人进行junit测试?junit和java:测试非公开方法

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

回答

0

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

8

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

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

+1

这是一个非常实用的解决方案,如果您没有奢侈的代码基础,每个班级都遵守SRP和所有其他设计指南。 – 2009-01-13 21:49:20

76

一个关于单元测试的学派认为,你应该只能测试公共方法,因为你应该只是单元测试你的公共API,并且通过这样做,你应该覆盖你的非代码 - 公共方法。你的旅费可能会改变;我发现有时候是这样,有时候并非如此。

随着中说,有一对夫妇的方式来测试非公共方法:

  • 可以测试保护和包范围的方法把你的单元测试在同一个包,因为他们“类重新测试。这是一个相当普遍的做法。
  • 您可以通过创建受测试类的子类来测试另一个包中的单元测试的受保护方法,该类将覆盖您想要测试为公共的方法,并让这些重写的方法使用super关键字调用原始方法。通常,这个“测试子类”将是JUnit TestCase类中的内部类,用于测试。在我看来,这样做有点冒失,但我已经做到了。

希望这会有所帮助。

+8

实际上,如果被测试的类位于src/box/of/Cats.java中,那么在测试项目中将测试类放到src/box/of/CatsTest.java并链接项目时,实际上不需要保护子类。这样你的测试类似乎在同一个包中,因此可以访问彼此的受保护方法。 – Esko 2009-01-14 07:22:57

+5

为了访问受保护的成员,提出了子类化的方法以符合(通常)被接受的将测试和源代码放在*不同*包中的做法。这使得指定在构建自动化工具的最终分发中排除单元测试变得很容易。 – Dinuk 2009-06-29 23:03:18

+15

@Dinuk:请注意,不要将测试和源代码放在不同的包中,您也可以将它们放到不同的源代码树中。这是如何Maven执行它(src/main/java和src/test/java)。这样,实现类和测试类可以在同一个包中,但可以轻松地分别处理构建/部署。 – sleske 2012-07-30 13:36:52

35

与许多单元测试问题一样,测试私有方法实际上是一个变相的设计问题。与其试图做任何棘手的事情来测试私有方法,当我发现自己希望为私有方法编写测试时,我花一分钟时间问自己:“我该如何设计这个,才能通过公共方法彻底测试它?”

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

9

如果您在相对较少的“公共”入口点下埋藏了大量逻辑,则很可能违反了Single Responsibility Principle。如果可能的话,你会想要将代码重构成多个类,最终导致更多的“公共”方法来测试。

3

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

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

以我的经验,需要单元测试私有方法是一个指示器,可以(而且应该)简化您正在测试的内容。

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

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

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

如果您担心测试具有大量逻辑的私有方法,那么您可以做的最聪明的事情是将该代码移入另一个公共方法的类中。一旦你这样做了,使用这个代码的以前的方法可以通过具有由存根或模拟提供的功能来简化测试。

24

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

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

自从我最初写这篇文章以来的三年中,我已经开始用稍微不同的方式来使用Java反射来解决问题。

肮脏的小秘密是,你可以用JUnit测试私有方法,就像使用反射一样公开的方法。您可以测试自己的内容,并且不会将其公开给客户。

0

与协议几乎每一个岗位 - 你或许应该重构,可能无法测试除了通过公私,只是想补充一个不同的方式来思考它...

认为你的类本身作为一个“单位”,而不是一个方法。您正在测试这个类,并且它可以维护一个有效的状态,而不管它是如何调用公共方法的。

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

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的某种内部逻辑,那么我认为这是不正确的 - 在我看来,这应该是私人的,并且使它在某种程度上更加可视化了代码。

5

这里是“可能不应该这样做”的方法,其他人都在不停地对你施加压力。不过,我认为这当然有可能的原因在于这样做。以下代码将访问专用字段,但私有方法的代码几乎相同。

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(); 
    } 

} 
2

从安迪·亨特借用一个想法,甚至是你的私有方法必须有一定的副作用,就是你感兴趣的,换句话说,它们必须从一些公共方法被调用和执行导致了一个有趣的任务你的对象的状态改变。测试该状态的变化。

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

1

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

0

作为这种分支的一种,我不确定每个人在整个“多语种编程”问题上的下落,但Groovy测试可以在Junit中运行,并忽略整个公共/非公共问题。值得注意的是,它被正式分类为“bug”,但是当他们试图修复它时,就会有这样的风暴,它会像原来一样被放回原处。

1

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

2

DP4j罐

为了测试私有方法,我们需要使用反射及其所有答案尖。

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

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

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

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

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

获取更多细节here

0

我绝对@duffymo代码应该从客户的观点进行测试(虽然他说他不干这样想的)同意。但是,从这个角度来看,私人与其他人有不同的含义。私有方法的客户端本身就是类,所以我更喜欢通过外部(公共/包保护)API测试它们。然而,受保护的和受软件包保护的成员在那里是为了外部客户,因此我使用继承拥有类或驻留在相同软件包中的假货测试它们。