單元測試幾個小建議

以下的示例均在DUNIT下進行,但同樣適用於XUNIT。只是語法用的是OBJECT PASCAL
最近在用DUNIT來寫程序,寫着寫着,也碰到了一些小問題,也找到過一些解決辦法,同時,也發現了不少的注意事項,特提出來與大家共勉。
1、最好用帶ID的異常來代替顯示對話框。
      優點:對話框無法進行捕捉。異常可以
      缺點:程序內部要加入異常處理機制,並要求拋出,代碼量增加
      舉例:
              當系統發現某員工不存在時,顯示‘該員工不存在,不能繼續’,這對應用程序來說沒有什麼問題,但如果在單元測試裏面要進行操作,就必須要得到這個提示或者以其它可以返回的狀態進行返回,加狀態字非常麻煩,我們可以定義一個異常類,然後,再進行拋出。這樣,在進行單元測試的時候,就可以進行捕捉了。
  EDisplayException = class(Exception)
  public
    ErrorID: Integer;
    constructor Create(Msg: string; ID: Integer); virtual;
  end;

constructor EDisplayException.Create(Msg: string; ID: Integer);
begin
  ErrorID := ID;
  Inherited Create(Msg);
end;

使用的時候,就可以在單元測試方法裏進行
try
  ..
except
  on e: EDisplayException do
  begin
      e.errorid.... 就可以想做什麼就做什麼
  end

2、分離窗體與程序
因爲窗體與程序結合得非常緊密的話,必須要做大量的手工操作,就達不到DUNIT自動測試的效果了。比如我們有這麼一段代碼。
procedure T1.Check(AType: string);
var
  tmpStr: string;    
begin
  tmpStr := InputBox('測試', '輸入', tmpStr);
  ......
end
因爲前面有一段要求用戶手工錄入的過程,使得我們的自動測試不好做,我們最好將它分離成兩個過程。
procedure T1.Check(AType: string);
var
  tmpStr: string;    
begin
  tmpStr := InputBox('測試', '輸入', tmpStr);
  T2(AType, tmpStr);
end

procedure T2.Check(AType: string; AValue: string);
begin
  ..
end
這樣,我們寫測試的時候,只要測試T2就可以了。

3、注意方法的顯示域。我們在單元測試的時候,可能經常需要測試到一個PRIVATE的方法,雖然不是非常喜歡,但還是會測試到,但我們的應用程序卻不允許它在PRIVATE裏面,怎麼辦呢,最好的辦法是加一個編譯指令,比如
  TSign = class
  {$IFDEF UNITTEST}
  public
  {$ELSE}
  private
  {$ENDIF}
    SignPrint: Boolean;
    WorkerMustExists: Boolean;

在我們的單元測試的工程裏面定義UNITTEST。
這樣就解決了我們需要的問題了。

4、獨立初始與判定。
對於可能會多次測試的項目,最好將初始化條件以及最後判定結果獨立。這樣可以使得它們重用。
比如我們有這樣的一個方法。
procedure TMyTest.Test1//對TMy測試
begin
   ...一大堆初始化

   My.Do;//這是調用被測試項。

   Check(..)..
   Check(..)..又一大堆判定
end;

在我們的程序裏面,可能另外有TMy2的Test3調用了TMy的Test1方法,對初始化變量還得做一次,我們將初始化及結果分離出來,就可以兩面兼顧了。形如

  TDataSwitchCheck = class
  public
    class procedure BeginDownSet;
    class procedure CheckDownSet(ACase: TTestCase);
end;
class procedure TDataSwitchCheck.BeginDownSet;
begin
  //先刪除服務器上的.
  SystemSetup.ServerAccess.ExecSQL('delete from p_pos_setup where dsection = ''單元測試''');

  SystemSetup.ServerAccess.ExecSQL('insert into p_pos_setup(dsection, dident, dvalue, dpos) '
                 + 'values(''單元測試'', ''測試'', ''測試'', ''99'')');

  SystemSetup.ServerAccess.ExecSQL('insert into p_pos_setup(dsection, dident, dvalue, dpos) '
                 + 'values(''單元測試'', ''測試'', ''測試'', ' + QuotedStr(SystemSetup.PosNo) + ')');

  //刪除本地上的
  SystemSetup.ClientAccess.ExecSQL('delete from p_pos_setup where dsection = ''單元測試''');

end;

class procedure TDataSwitchCheck.CheckDownSet(ACase: TTestCase);
var
  tmpString: string;
begin
  SystemSetup.ClientAccess.GetSQLValue('select dsection from p_pos_setup where dsection = ''單元測試'' and dpos = ' + QuotedStr(SystemSetup.PosNo) , tmpString);
  ACase.Check(tmpstring = '單元測試', '下載本POS機設置錯誤');

  SystemSetup.ClientAccess.GetSQLValue('select dsection from p_pos_setup where dsection = ''單元測試'' and dpos = ''99''', tmpString);
  ACase.Check(tmpstring = '', '下載了不屬於本POS機設置錯誤');
end;

//以下是測試用例
procedure TDataSwitchTests.TestDownSet;
begin
  TDataSwitchCheck.BeginDownSet;    //初始化

  SystemSetup.DataSwitch.DownSet(nil);// 這是被測試的一個方法。

  TDataSwitchCheck.CheckDownSet(Self) //檢查結果
end;

//另一個測試用例。
procedure TFrm_DataSwtichTests.TestStartSwtich;
begin
  TDataSwitchCheck.BeginDownRight;
  TDataSwitchCheck.BeginDownSet;
  TDataSwitchCheck.BeginDownKey;
  TDataSwitchCheck.BeginUpdateSet;
  TDataSwitchCheck.BeginSaleDataUp;

  TFrm_DataSwtich.AllDataSwtich;

  TDataSwitchCheck.CheckDownRight(Self);
  TDataSwitchCheck.CheckDownSet(Self);
  TDataSwitchCheck.CheckDownKey(Self);
  TDataSwitchCheck.CheckUpdateSet(Self);
  TDataSwitchCheck.CheckSaleDataUp(Self);

end;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章