第3章 使用DBX4的測試框架類別(2) : 如何客製化自動產生測試資料的流程

面展示了如何使用TDbxDataGenerator自動產生測試資料,雖然DBX4框架提供了簡單的自動產生資料的能力,但在實際的開發中開發人員一定會需要產生有意義的測試資料,那麼開發人員如何能夠讓TDbxDataGenerator使用開發人員的方式來產生測試資料呢? 答案就在TDBXDataGeneratorColumn類別。
    當TDbxDataGenerator要產生測試資料時,它會使用DBX4的元資料相關類別來判斷欲產生測試資料的資料表的結構,然後根據資料表的欄位型態來建立TDBXDataGeneratorColumn衍生類別的物件來產生測試資料。也許讓我們來看看TDBXDataGeneratorColumn類別的定義,再以解釋讀者就更容易瞭解這整個流程。
下面是TDBXDataGeneratorColumn的定義,它是一個抽象類別,其中定義了許多的虛擬方法以便讓衍生類別繼承並且加以實作。

 TDBXDataGeneratorColumn = class abstract
  public
    constructor Create(const InMetaDataColumn: TDBXMetaDataColumn);
    procedure Open; virtual;
    destructor Destroy; override;
    function GetString(const Row: Int64): UnicodeString; virtual; abstract;
    function GetBoolean(const Row: Int64): Boolean; virtual;
    function GetInt8(const Row: Int64): Byte; virtual;
    function GetInt16(const Row: Int64): SmallInt; virtual;
    function GetInt32(const Row: Int64): Integer; virtual;
    function GetInt64(const Row: Int64): Int64; virtual;
    function GetDouble(const Row: Int64): Double; virtual;
    function GetSingle(const Row: Int64): Single; virtual;
    function GetBytes(const Row: Int64): TBytes; virtual;
    function GetDecimal(const Row: Int64): UnicodeString; virtual;
    function GetYear(const Row: Int64): Integer; virtual;
    function GetMonth(const Row: Int64): Integer; virtual;
    function GetDay(const Row: Int64): Integer; virtual;
    function GetHour(const Row: Int64): Integer; virtual;
    function GetMinute(const Row: Int64): Integer; virtual;
    function GetSeconds(const Row: Int64): Integer; virtual;
    function GetMilliseconds(const Row: Int64): Integer; virtual;
    procedure SetValue(const Row: Int64; const Value: TDBXWritableValue); virtual; abstract;
  protected
    procedure SetDataGenerator(const DataGenerator: TDBXCustomDataGenerator); virtual;
    function GetColumnName: UnicodeString; virtual;
    function GetDataType: Integer; virtual;
    function GetMetaDataColumn: TDBXMetaDataColumn; virtual;
  private
    function TypeNotSupported: TDBXDataGeneratorException;
  protected
    FMetaDataColumn: TDBXMetaDataColumn;
    FDataGenerator: TDBXCustomDataGenerator;
  public
    property DataGenerator: TDBXCustomDataGenerator write SetDataGenerator;
    property ColumnName: UnicodeString read GetColumnName;
    property DataType: Integer read GetDataType;
    property MetaDataColumn: TDBXMetaDataColumn read GetMetaDataColumn;
  end;

例如假設欲產生測試資料的資料表的第一個欄位型態是整數序列號,那麼TDbxDataGenerator便會建立TDBXInt32SequenceGenerator的物件來產生測試資料,由於TDBXInt32SequenceGenerator是提供產生整數序列號的測試資料,因此它只複載了TDBXDataGeneratorColumn的GetInt32虛擬方法以提供整數序列號數值,複載GetString虛擬方法以字串的型態提供整數序列號數值, TDBXInt32SequenceGenerator並不需要複載所有TDBXDataGeneratorColumn的虛擬方法。

  TDBXInt32SequenceGenerator = class(TDBXDataGeneratorColumn)
  public
    constructor Create(const MetaData: TDBXMetaDataColumn);
    procedure Open; override;
    function GetInt32(const Row: Int64): Integer; override;
    function GetString(const Row: Int64): UnicodeString; override;
    procedure SetValue(const Row: Int64; const Value: TDBXWritableValue); override;
  end;

而例如假設欲產生測試資料的資料表的第一個欄位型態是字串型態,那麼TDbxDataGenerator便會建立TDBXWideStringSequenceGenerator的物件來產生測試資料,TDBXWideStringSequenceGenerator的定義如下,它的使用方式和前面介紹的TDBXInt32SequenceGenerator是一樣的,只是它是負責提供產生字串型態的測試資料:

  TDBXWideStringSequenceGenerator = class(TDBXDataGeneratorColumn)
  public
    constructor Create(const MetaData: TDBXMetaDataColumn);
    procedure Open; override;
    function GetString(const Row: Int64): UnicodeString; override;
    procedure SetValue(const Row: Int64; const Value: TDBXWritableValue); override;
  end;

在DBX4框架中類似TDBXInt32SequenceGenerator類別以提供各種不同型態的測試資料類別超過10個以上,而且開發人員還可以自己定義/實作客製化類別,稍後我們就會討論。下面的表格說明了這些類別:

類別

說明

TDBXBooleanSequenceGenerator

提供產生布林值型態的測試資料

TDBXBlobSequenceGenerator

提供產生Blob型態的測試資料

TDBXAnsiStringSequenceGenerator

提供產生Ansi字串型態的測試資料

TDBXDateSequenceGenerator

提供產生日期型態的測試資料

TDBXDecimalSequenceGenerator

提供產生Decimal型態的測試資料

TDBXDoubleSequenceGenerator

提供產生Double型態的測試資料

TDBXInt16SequenceGenerator

提供產生16位元整數型態的測試資料

TDBXInt32SequenceGenerator

提供產生32位元整數型態的測試資料

TDBXInt64SequenceGenerator

提供產生64位元整數型態的測試資料

TDBXInt8SequenceGenerator

提供產生8位元型態的測試資料

TDBXTimeSequenceGenerator

提供產生時間型態的測試資料

TDBXTimestampSequenceGenerator

提供產生Timestamp型態的測試資料

TDBXWideStringSequenceGenerator

提供產生WideString/UnicodeString型態的測試資料

讓我們看看TDBXWideStringSequenceGenerator如何產生測試資料,下面是TDBXWideStringSequenceGenerator的GetString方法,它會在TDbxDataGenerator要為字串型態的欄位產生測試資料時呼叫:

function TDBXWideStringSequenceGenerator.GetString(const Row: Int64): UnicodeString;
var
  Value: UnicodeString;
begin
  Value := ‘W’ + IntToStr(Row);
  if FMetaDataColumn.FixedLength then
    while Length(Value) < FMetaDataColumn.Precision do
      Value := Value + ‘ ‘;
  Result := Value;
end;

從上面的實作程式碼可以知道,TDBXWideStringSequenceGenerator.GetString在產生測試資料時是以’W’字元為起頭並且加入正在產生測試資料的列行數的數值,例如為第一筆資料產生的是W1,第2筆就是W2等以此類推,這也解釋了為什麼圖3-2中『研討會名稱』和『主講人』欄位的測試資料就是這樣的內容。
瞭解了上面討論的原理之後,那麼我們如何讓TDbxDataGenerator產生我們需要的測試資料而不是內定的簡單而無意義的測試資料呢? 答案很簡單,我們只需要進行下面的兩項工作:

  1. 實作從TDBXDataGeneratorColumn衍生的類別,在這個衍生的類別中使用程式碼或是其他方式(例如從檔案中載入測試資料)來產生測試資料
  2. 通知TDbxDataGenerator建立我們實作的衍生的類別來為測試資料表的欄位來產生測試資料

首先讓我們看看如何完成上面的第二個步驟,這個工作很簡單,我們只需要建立客製化TDBXDataGeneratorColumn衍生類別物件,並且加入到TDbxDataGenerator物件之中即可。
例如在下面的程式碼中,我們首先在047行中藉由元資料類別取得測試資料表的每一個欄位物件,然後在049到067行中一一的判斷每一個欄位物件的資料型態,接著就建立相對應的TDBXDataGeneratorColumn客製化衍生類別物件,再呼叫TDbxDataGenerator的AddColumn方法把它加入到TDbxDataGenerator物件之中。

001    procedure TForm5.加入欄位物件(aProvider : TDBXMetaDataProvider; DataGenerator: TDbxDataGenerator);
002   
003      function 建立整數欄位(cName : string) : TDBXInt32SequenceGenerator;
004      var
005        col : TDBXInt32Column;
006      begin
007        col := TDBXInt32Column.Create(cName);
008        try
009          Result := TDBXInt32SequenceGenerator.Create(col);
010        finally
011          FreeAndNil(col);
012        end;
013      end;
014   
015      function 建立字串欄位(cName : string; iLength : Integer) : TDBXWideStringSequenceGenerator;
016      var
017        col : TDBXUnicodeVarCharColumn;
018      begin
019        col := TDBXUnicodeVarCharColumn.Create(cName, iLength);
020        try
021          if (col.ColumnName = ‘主講人’) then
022            Result := T主講人產生器.Create(col)
023          else
024            if (col.ColumnName = ‘研討會名稱’) then
025              Result := T研討會產生器.Create(col)
026            else
027              Result := TDBXWideStringSequenceGenerator.Create(col);
028        finally
029          FreeAndNil(col);
030        end;
031      end;
032   
033      function 建立日期欄位(cName : string) : TDBXDateSequenceGenerator;
034      var
035        col : TDBXDateColumn;
036      begin
037        col := TDBXDateColumn.Create(cName);
038        try
039          Result :=TDBXDateSequenceGenerator.Create(col);
040        finally
041          FreeAndNil(col);
042        end;
043      end;
044    var
045      cols : TDBXColumnsTableStorage;
046    begin
047      cols := 取得欄位物件(aProvider, TESTTABLENAME);
048   
049      while (cols.InBounds) do
050      begin
051        case cols.DbxDataType of
052          TDBXDataTypes.UnknownType, TDBXDataTypes.Int32Type :
053          begin
054            DataGenerator.AddColumn(建立整數欄位(cols.ColumnName));
055          end;
056          TDBXDataTypes.WideStringType :
057          begin
058            DataGenerator.AddColumn(建立字串欄位(cols.ColumnName, cols.Precision));
059          end;
060          TDBXDataTypes.DateType :
061          begin
062            DataGenerator.AddColumn(建立日期欄位(cols.ColumnName));
063          end;
064        end;
065        cols.Next;
066      end;
067    end;

那麼步驟1如何完成? 也很簡單,我們只需要定義從TDBXDataGeneratorColumn或是它的衍生類別繼承下來的客製化類別,再於其中撰寫如何產生測試資料的程式碼即可。
例如下面就是上面程式碼使用的『T研討會產生器』類別,這個類別負責在測試資料表的『研討會名稱』欄位中產生測試資料:

001    unit u研討會產生器;
002   
003    interface
004    uses
005      DBXCommon,
006      DBXCommonTable,
007      DBXMetaDataProvider,
008      SysUtils,
009      DBXCustomDataGenerator;
010   
011    type
012      T研討會產生器 = class(TDBXWideStringSequenceGenerator)
013      public
014        constructor Create(const MetaData: TDBXMetaDataColumn);
015        procedure Open; override;
016        function GetString(const Row: Int64): UnicodeString; override;
017        procedure SetValue(const Row: Int64; const Value: TDBXWritableValue); override;
018      private
019        dataArray : array[0..5] of UnicodeString;
020        iPos : Integer;
021        procedure InitailizeData;
022      end;
023   
024    implementation
025   
026    { TDBXMyWideStringSequenceGenerator }
027   
028    constructor T研討會產生器.Create(
029      const MetaData: TDBXMetaDataColumn);
030    begin
031      inherited Create(MetaData);
032      InitailizeData;
033    end;
034   
035    function T研討會產生器.GetString(
036      const Row: Int64): UnicodeString;
037    begin
038      Result := dataArray[iPos mod 6];
039      Inc(iPos);
040    end;
041   
042    procedure T研討會產生器.InitailizeData;
043    begin
044      dataArray[0] := ‘Delphi 2009產品技術發表會’;
045      dataArray[1] := ‘C++Builder 2009產品技術發表會’;
046      dataArray[2] := ‘Delphi 2009 Unicode程式設計’;
047      dataArray[3] := ‘Delphi 2009 DataSnap程式設計’;
048      dataArray[4] := ‘C++Builder 2009 Unicode程式設計’;
049      dataArray[5] := ‘C++Builder 2009 DataSnap程式設計’;
050      iPos := 0;
051    end;
052   
053    procedure T研討會產生器.Open;
054    begin
055      inherited open;
056    end;
057   
058    procedure T研討會產生器.SetValue(const Row: Int64;
059      const Value: TDBXWritableValue);
060    begin
061      Value.SetWideString(GetString(Row));
062    end;
063   
end.

正是由於我們提供了客製化的『T研討會產生器』類別,因此TDbxDataGenerator才在圖3-3中產生了客製化的測試資料,如何?整個客製化的流程很簡單吧。

Ok,希望現在各位就瞭解如何使用和整合DBX4框架來自動測試任何的測試資料了。

廣告
  1. #1 by junhome on 2008 年 11 月 08 日 - 03:55:00

    维哥,可不可以不再研究dbx,研究点别的东东,如2009的数据建模什么的,dbx已经被证明是一个垃圾产品,再美化人们也不认头的.

  2. #2 by xyzhq on 2008 年 11 月 10 日 - 01:31:51

    用delphi2007的dbx开发的产品挺好的,怎么会是垃圾产品?分发简单,与其他产品的不同版本dbx可以并存,执行效率也挺好的,比bde进步多了。希望大师继续关注 delphi2009 中的dbx.当然,研究一下delphi2009中的数据建模,也是非常好的

  3. #3 by on 2008 年 11 月 10 日 - 04:33:55

    >dbx已经被证明是一个垃圾产品,再美化人们也不认头的.我不知道你的證明是從那裡來凡的, 我的案子就是用dbExpress, 所以我研究它, 精逍它. dbExpress是不完美, 但卻是目前原生Win32下才最好的方案之一.

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

%d 位部落客按了讚: