比如说,Bird是我们要测试的class,它有public, protected,以及private的方法。
// 文件位置:src/test/sample/Bird.java package test.sample; class Bird { public void fly() { ... } public void eat() { ... } protected void singRandomly() { final Song s = findASong(<some_random_number>); singASong(s); } private Song findASong() { ... } private void singASong() { ... } }
现在有一个 BirdTest
class。对这个class而言,它可见的所有函数,是 Bird.class.getDeclaredMethods()
的返回值。
代码细节请看
junit.internal.MethodSorter#getDeclaredMethods()
http://grepcode.com/file/repo1.maven.org/maven2/junit/junit/4.12/org/junit/internal/MethodSorter.java#MethodSorter.getDeclaredMethods%28java.lang.Class%29
以及java.lang.Class#getDeclaredMethods()
所有的 public, protected, private
方法 BirdTest
都能看到。但是, 看到不等于能调用 。
// 文件位置:tst/test/sample/BirdTest.java package test.sample; class BirdTest { @Test public void testFly_CaseDescription1() { ... bird.fly(); //当然ok,因为Bird#fly是public的 } @Test public void testSingRandomly_CaseDescription1() { ... bird.sing(); //ok,因为BirdTest也在test.sample package下面。否则是非法的。 } @Test public void testFindASong() { ... bird.findASong(); // 非法,不能调用Bird的私有函数。 } }
在上面的代码里,由于 BirdTest
与 Bird
在一个package test.sample
里,所以 Bird
所有的 public
和 protected
函数,对 BirdTest
可见。但是, private
应该是不可调用的。
当然,有人会告诉你如何利用java reflection的API来让 private
method也可以调用
// 无关紧要的parameter用 '_' 略去了 Method findASong = targetClass.getDeclaredMethod("findASong", _); findASong.setAccessible(true); return findASong.invoke(_, _);
但是,这打破了 Bird
类的封装,是非常不好的。设想,改动 private
的方法的声明会造成test failure,那么 private
就失去意义了。与 protected
其实区别不大。
那么应该怎么做呢?
不去测试private函数,好的private函数都应该是很小很简单的,测试那调用了private函数的public和protected方法即可。
或者,也许这个private函数其实应该被声明称protected。
如果以上方法你都觉得不合适,而某一个private函数很复杂,很需要测试。那么,根据Single Responsibility原则,这个private函数就应该被放到一个单独的class里面。
class BirdSongs { protected Song findASong(Integer id) { ... } protected void singASong(Song s) { ... } }
然后,对 BirdSongs#findASong
进行测试。
如果您有不同意见,欢迎与我讨论。