[深刻JUnit] 爲何別測試private函數

好比說,Bird是咱們要測試的class,它有public, protected,以及private的方法。java

// 文件位置: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()的返回值。dom

代碼細節請看
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()
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b27/java/lang/Class.java#Class.getDeclaredMethods%28%29maven

全部的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的私有函數。
  }
}

在上面的代碼裏,因爲BirdTestBird在一個package test.sample裏,因此Bird全部的publicprotected函數,對BirdTest可見。可是,private應該是不可調用的。測試

固然,有人會告訴你如何利用java reflection的API來讓private method也能夠調用code

// 可有可無的parameter用 '_' 略去了
Method findASong = targetClass.getDeclaredMethod("findASong", _);
findASong.setAccessible(true);
return findASong.invoke(_, _);

可是,這打破了Bird類的封裝,是很是很差的。設想,改動private的方法的聲明會形成test failure,那麼private就失去意義了。與protected其實區別不大。ip

那麼應該怎麼作呢?get

  • 不去測試private函數,好的private函數都應該是很小很簡單的,測試那調用了private函數的public和protected方法便可。it

  • 或者,也許這個private函數其實應該被聲明稱protected。io

若是以上方法你都以爲不合適,而某一個private函數很複雜,很須要測試。那麼,根據Single Responsibility原則,這個private函數就應該被放到一個單獨的class裏面。

class BirdSongs {
  protected Song findASong(Integer id) { ... }
  protected void singASong(Song s) { ... }
}

而後,對BirdSongs#findASong進行測試。

若是您有不一樣意見,歡迎與我討論。

相關文章
相關標籤/搜索