在Method中有new 底層的物件或服務。這些物件可能必須配合系統的環境才可生成,在測試環境也許生不出這些物件。
假設公司有一個遠端服務(remote service),當輸入計畫編號時,該服務會回傳"計畫"(Project)這個物件。因這項遠端服務具有機密性,所以只能在特定的環境下使用,在開發或測試環境是無法呼叫得到。另外這個服務的相關Class和設定都已被包在一個 Jar 檔裏。要使用這項遠端服務,只要呼叫相關的API即可,其它設定都不需更動。
這個遠端服務有一個Interface 為IRemote,並有一個getProject(String prjNo) 方法。程式碼如下:
public interface IRemote { Project getProject(String prjNo); }
在自行開發的程式中有一項服務(AppService),需透過遠端服務來取得計畫預算,所以程式碼可能會這樣寫:
public class AppService implements IService{ public Long getPrjBudget(String prjNo){ IRemote remote=new RemoteProxy(); Project project=remote.getProject(prjNo); return project.getBudget(); } }如果程式碼是這樣寫,那測試就不會過,因為測試環境根本無法呼叫到遠端服務,而且在測試環境中,程式也跑不起來。程式中,IRemote remote=new RemoteProxy() 就跟getPrjBudget(..) 這個方法綁死無法抽換。只要在方法中有用到 new 來建立服務,那這個服務就會跟這個方法綁死而無法抽換。
如果我們把程式改寫如下:
public class AppService implements IService{ private IRemote remote; public void setRemote(IRemote remote){ this.remote=remote; } public Long getPrjBudget(String prjNo){ Project project=remote.getProject(prjNo); return project.getBudget(); } }
這樣遠端服務就沒有跟getPrjBudget()這個方法綁死,而是變得靈活可抽換,當測試時就可以提供假的遠端服務來測試程式的邏輯,這樣單元測試就能執行,而且在測試環境中也能夠將程式跑起來。
依環境取得AppService:
public AppService getService(){ AppService service=new AppService(); if(isDevelopment()){ //取得測試環境的遠端服務 service.setRemote(getMockRemote()); }else{ //取得正式環境的遠端服務 service.setRemote(new RemoteProxy()); } return service; }
AppService 的單元測試:
public class AppServiceTest{ private AppService service; @Before public void setup(){ service=new AppService(); service.setRemote(getMockRemote()); } @Test public void testGetPrjBudget(){ String prjNo="A123"; Long budget=service.getPrjBudget(prjNo); assertTrue(budget>0); } }
沒有留言:
張貼留言