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
}
}
17 回答
关于单元测试的一个思想学派说,你应该只能测试公共方法,因为你应该只对你的公共API进行单元测试,并且通过这样做,你应该覆盖非公开方法中的代码 . 你的旅费可能会改变;我发现有时候这种情况有时并非如此 .
话虽如此,有几种方法可以测试非公开方法:
您可以通过将单元测试与他们正在测试的类放在同一个包中来测试protected和package-scope方法 . 这是一种相当普遍的做法 .
您可以通过创建一个被测试类的子类来测试另一个包中单元测试的受保护方法,该子类将要测试的方法覆盖为public,并让这些重写方法使用super关键字调用原始方法 . 通常,这个"testing subclass"将是进行测试的JUnit TestCase类中的内部类 . 在我看来,这有点多哈,但我已经做到了 .
希望这可以帮助 .
与许多单元测试问题一样,测试私有方法实际上是伪装的设计问题 . 当我发现自己希望为私人方法编写测试时,我花了一分钟时间问自己,“我怎么需要设计这个以便我可以通过公共方法彻底测试它?”而不是尝试做任何棘手的测试私有方法 .
如果这不起作用,JUnitX允许测试私有方法,虽然我相信它只适用于JUnit 3.8 .
当你编写一个JUnit测试时,你必须做一个微妙的思维转变:“我现在是我自己 class 的客户 . ”这意味着私有是私有的,您只测试客户端看到的行为 .
如果该方法确实应该是私有的,我认为这只是为了测试而使其可见的设计缺陷 . 您必须能够根据客户端看到的内容推断其正确的操作 .
在我最初写这篇文章之后的三年里,我开始使用Java反射略微区别地解决问题 .
肮脏的小秘密是,您可以使用反射在JUnit中测试私有方法,就像使用公共方法一样 . 您可以测试您的内心,但仍然不会将它们公开给客户 .
最简单的解决方案是将JUnit测试放在同一个包(但不同的目录)中,并使用默认(即包私有)方法 .
另一种更复杂的方法是使用反射来访问私有方法 .
如果您在相对较少的"Public"入口点处埋藏了大量逻辑,则可能违反了Single Responsibility Principle . 如果可能,您需要将代码重构为多个类,最终导致更多"Public"方法从中进行测试 .
以下是其他人一直在喋喋不休的“可能不应该这样做”的方法 . 不过,我认为有可能存在这样做的理由 . 以下代码将访问私有字段,但私有方法的代码几乎相同 .
我几乎总是在我的Java项目中使用Spring,因此,我的对象是为依赖注入而构建的 . 它们往往是在应用程序上下文中组装的公共接口的相当精细的实现 . 因此,我很少(如果有的话)需要测试私有方法,因为类本身足够小,它根本不是问题 .
即使我不使用Spring,我倾向于采用相同的方法将小而简单的对象组装成越来越大的抽象,每个抽象都相对简单,但是由聚合对象变得复杂 .
根据我的经验,需要对私有方法进行单元测试,这表明您正在尝试的内容可以(并且应该)进行简化 .
那就是,如果你真的觉得有必要:
受保护的方法可以通过子类进行测试;
可以通过将单元测试放在同一个包中来测试包私有方法;和
私人方法可以通过提供单元测试,例如,包私有工厂代理方法 . 不理想,但私人确实意味着私人 .
您通常不测试私有方法,因为它们只能(通常)通过另一个公共方法间接测试 . 当你试驾并制作私人方法时,它们通常是“提取方法”重构的结果,并且已经被间接测试 .
如果您担心使用大量逻辑测试私有方法,那么您可以做的最聪明的事情就是将该代码移动到公共方法中的另一个类中 . 完成之后,使用此代码的先前方法可以通过使用存根或模拟提供的功能来简化测试 .
借用Andy Hunt的想法,即使你的私有方法也必须有一些你感兴趣的副作用 . 换句话说,必须从一些公共方法调用它们并执行一个有趣的任务,导致你的对象的状态发生变化 . 测试该状态的变化 .
假设您有公共方法pubMethod和私有方法privMethod . 当你调用pubMethod时,它会调用privMethod来执行一个任务(也许解析一个String) . 然后,pubMethod使用此解析的String以某种方式设置成员变量的值或影响其自身的返回值 . 通过观察对pubMethod的返回值或成员变量的期望效果进行测试(可能通过使用访问器来获取它们) .
为了测试私有方法,我们需要使用反射,并将其指向所有答案 .
好吧现在这个任务在Dp4j jar的帮助下得到了简化 .
Dp4j分析您的代码并自动为您生成Reflection API代码 .
只需将dp4j.jar添加到CLASSPATH即可 .
Dp4j.jar包含注释处理器,它们将在代码中查找使用@Test JUnit注释注释的方法 .
Dp4j分析这些方法的代码,如果它发现你是非法访问私有方法,它将用使用Java的Reflection API的等效代码替换你的无效私有方法引用 .
获取更多详情here
我遇到了同样的问题,“如果它需要私有,它可能应该被重构”并不适合我 .
假设您有某种功能需要以某种方式在类内部分离出来 . 例如,假设我有这样的事情:
这里的要点是,junit迫使我将流程公之于众,或至少受到保护,或将其置于自己的实用类中 . 但是如果它是HolderOfSomeStrings的某种内部逻辑,那对我来说并不清楚这是正确的 - 在我看来,这应该是私有的,并且在某种程度上使代码变得更加可见 .
您可以使用TestNG而不是JUnit,它不关心私有或公共方法 .
使用上面的反射来测试私有方法 . 如果我们遵循TDD,我们应该测试私有方法,因为TDD意味着以后不会出现意外 . 因此,不应该等待完成他的公共方法来测试私有 . 这有助于在重新分解时进行更细粒度的回归测试 .
寻找“PrivateAccessor.invoke” . 我的代码从“junitx.util”导入它,但我不知道它来自哪里 .
几乎与每一篇文章一致 - 你应该重构并且可能不会通过公开测试私有,只是想添加一种不同的方式来思考它...
将您的课程本身视为“单元”,而不是方法 . 您正在测试该类,并且无论如何调用公共方法,它都可以保持有效状态 .
调用私有方法可以破坏封装并实际使测试无效 .
作为这种分支的一种分支,我不确定每个人都在整个“多语言编程”问题,但Groovy测试可以在Junit中运行,并忽略整个公共/非公共问题 . 侧面说明,它被正式归类为“虫子”,但是当他们试图修复它时,就会发生这样一场暴风雨,它被放回原来的状态 .
我绝对同意@duffymo应该从客户的角度测试代码(尽管他说他不会这样思考) . 但是,从这个角度来看,私人与其他人有不同的含义 . 私有方法的客户端是类本身,所以我更喜欢通过外部测试它们(公共/包保护)API . 但是,受保护和受包保护的成员可以用于外部客户端,因此我使用伪继续拥有类或驻留在同一个包中的伪造来测试它们 .