綠燈 | 紅燈

妙思資訊

2012年4月3日 星期二

你開發的程式好測試嗎(七)?

第六種情況

測試  private method.

因 private method 不好測,若要進行單元測試可採用以下幾種方式:
1. 透過 public method 間接呼叫 private method 來測試.
2. 如果private method 可以改成default method ,只要source class 和test class有相同的package 名稱,即可對default method 進行測試。
3. 使用reflection 直接對private method 進行測試。

透過 public method 間接呼叫 private method 來測試


使用public method 來間接測試 private method 是最簡易的方式。但是如果public method 沒有管道取得或回傳private method 的結果,那也是無法測;另外,比較不好的原因是2個method couple 在一起,萬一將來private method 改了,為了要能測試 private method ,public method 有可能也會一起改。

例:  public method getTenant(String name) 間接呼叫 private method findByName(String name)

public Tenant getTenant(String name){
  
   return findByName(name);
}  
 
private Tenant findByName(String name){
  
    return dao.getTenantByName(name);
  
}

直接呼叫public method getTenant() 來測試
@Test
public void testPrivateMethodByPublicMethod(){
  
 String name = "Tom";
 Tenant tenant=service.getTenant(name);
 assertEquals(name, tenant.getName());
}

使用 default method 來測試


將 private method 改為default method,並將source class和 test class 的package 設成一致,便可直接對此default method 進行測試。

例:   下圖中要測試的是RentServiceImpl 的 default method  findByName(String name) 。且Test Class (DefaultMethodTest) 和 Source Class (RentServiceImpl) 的package name 皆為 rent.report

    

//default method
Tenant findByName(String name) {

 return dao.getTenantByName(name);

}

對default method 進行單元測試
package rent.report;

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import rent.demo.Tenant;

public class DefaultMethodTest {

 private RentServiceImpl service;

 @Before
 public void setUp() {

  service = new RentServiceImpl();  
 } 
 
 @Test
 public void testPrivate2DefaultMethod() {
  String name = "Tom";
  Tenant tenant = service.findByName(name);
  assertEquals(name, tenant.getName());
 }

}

使用reflection 直接對private method 進行測試

最後,如果對上述二種測試方式都不滿意,那就只能利用reflection 的方式直接對private method進行測試。如果不想自己寫reflection 的程式碼,最方便的方法就是去下載 junit-addons  可以經由  PrivateAccessor 的 invoke() 來呼叫 private method.

invoke(Object object, String methodName, Class[] parameterTypes, Object[] args) 
第一個參數 object: 有此待測方法的物件. 
第二個參數 methodName:待測方法
第三個參數 parameterTypes:傳入待測方法的參數型態
第四個參數 args :傳入待測方法的參數值

以 RentServiceImpl 的 findByNme(String name) 為例,第一個參數就是 RentServiceImple 的 instance service;第二個參數就是 "findByName"; 第三個參數就是  new Class[] { String.class }; 第四個參數就是new Object[]{"name"}.

例: 測試  private Tenant findByName(String name)

@Test
public void testPrivateMethod() throws Throwable {

 String name = "Tom";
 Tenant tenant = (Tenant) PrivateAccessor.invoke(service, "findByName",
    new Class[] { String.class }, new Object[] { name});
  
 assertTrue(tenant.getName().equals(name));
}
最後要注意的是,當進行refactory 將此private method name變更時,因invoke 的第二個參數是使用method 的字串名稱,所以不會隨refactory 同步更換,要自己手動將此method 名稱變更為refactory後的名稱才行。

沒有留言: