DUnit Demos

Simple Math Library

Create code for a simple math library and test it. Methods include Add, divide, subject and multiply

Refactoring

Refactor code that is part of your interface but should be in separate classes.

We start out (TFormWorst) with code that is very repetitious. Note that the first four lines in each procedure is identical.

procedure TFormWorst.ButtonDaysPassedClick(Sender: TObject);
begin
  FDay := StrToInt(EditDay.Text);
  FMonth := StrToInt(EditMonth.Text);
  FYear := StrToInt(EditYear.Text);
  FDateTime := EncodeDateTime(FYear, FMonth, FDay, 0, 0, 0, 0);
  EditDaysPassed.Text := IntToStr(DaysBetween(Now, FDateTime));
end;


procedure TFormWorst.ButtonHoursPassedClick(Sender: TObject);
begin
  FDay := StrToInt(EditDay.Text);
  FMonth := StrToInt(EditMonth.Text);
  FYear := StrToInt(EditYear.Text);
  FDateTime := EncodeDateTime(FYear, FMonth, FDay, 0, 0, 0, 0);
  EditHours.Text := IntToStr(DaysBetween(Now, FDateTime) * 24);
end;

In the next step (TFormBetter), we factor out the code that is overly repetitious into its own method:

procedure TFormBetter.GetData();
begin
  FDay := StrToInt(EditDay.Text);
  FMonth := StrToInt(EditMonth.Text);
  FYear := StrToInt(EditYear.Text);
  FDateTime := EncodeDateTime(FYear, FMonth, FDay, 0, 0, 0, 0);
end;

Then we create two methods for handling the getting days and hours passed chores:

function TFormBetter.GetDaysPassed: Integer;
begin
  Result := DaysBetween(Now, FDateTime);
end;


function TFormBetter.GetHoursPassed: Integer;
begin
  Result := GetDaysPassed * 24;
end;

Though we have added to the total amount of code we have written we can see the advantage we have created when we look at the button response methods. Both of them are now much simpler, and much easier to understand:

procedure TFormBetter.ButtonDaysPassedClick(Sender: TObject);
begin
  GetData();
  EditDaysPassed.Text := IntToStr(GetDaysPassed());
end;


procedure TFormBetter.ButtonHoursPassedClick(Sender: TObject);
begin
  GetData();
  EditHours.Text := IntToStr(GetHoursPassed());
end;

The final refactoring that we must complete will enable us to place all this code in a separate class.

unit WorkObject;

interface

uses
  SysUtils, DateUtils;

type
 	{ Should be a private class
  	A Simple date object, which encapsulates FDateTime }
  TWorkDate = class(TObject)
  private
    FDateTime: TDateTime;
    FYear: Integer;
    FMonth: Integer;
    FDay: Integer;
  public
    constructor Create(const AMonth: Integer; const ADay: Integer; const AYear: Integer);
    procedure Init(const AMonth: Integer; const ADay: Integer; const AYear: Integer);
    property DateTime: TDateTime read FDateTime write FDateTime;
    property Day: Integer read FDay write FDay;
    property Month: Integer read FMonth write FMonth;
    property Year: Integer read FYear write FYear;
  end;

  { A class that can perform certain operations on a TWorkDate Object.
    	GetDayspassed Since a given date
    	GetHousePassed since a given date.
    	Get the current data }
  TWorkObject = class(TObject)
  private
    FWorkDate: TWorkDate;
  public
    constructor Create(); overload;
    constructor Create(const AMonth: Integer; const ADay: Integer; const AYear: Integer); overload;
    constructor Create(AWorkDate: TWorkDate); overload;
    procedure Init(); overload;
    procedure Init(const AMonth: Integer; const ADay: Integer; const AYear: Integer); overload;
    function GetDay: Integer;
    function GetDaysPassed: Integer;
    function GetHoursPassed: Integer;
    function GetMonth(): Integer;
    function GetTodaysDate(): String;
    function GetYear(): Integer;
  end;

implementation

{ TWorkDate }

{ Constructor to use to initialize the dates }
constructor TWorkDate.Create(const AMonth, ADay, AYear: Integer);
begin
  Init(AMonth, ADay, AYear);
end;

procedure TWorkDate.Init(const AMonth, ADay, AYear: Integer);
begin
  FYear := AYear;
  FMonth := AMonth;
  FDay := ADay;
end;

{ TWorkObject }

constructor TWorkObject.Create;
begin
  Init(0, 0, 0);
end;

constructor TWorkObject.Create(const AMonth, ADay, AYear: Integer);
begin
  Init(AMonth, ADay, AYear);
end;

constructor TWorkObject.Create(AWorkDate: TWorkDate);
begin
  FWorkDate := AWorkDate;
  Init();
end;

function TWorkObject.GetDay: Integer;
begin
  Result := FWorkDate.Day;
end;

function TWorkObject.GetDaysPassed: Integer;
begin
  Result := DaysBetween(Now, FWorkDate.DateTime)
end;

function TWorkObject.GetHoursPassed: Integer;
begin
  Result := GetDaysPassed * 24;
end;

function TWorkObject.GetMonth: Integer;
begin
  Result := FWorkDate.Month;
end;

function TWorkObject.GetTodaysDate: String;
begin
  Result := DateTimeToStr(Now);
end;

function TWorkObject.GetYear(): Integer;
begin
  Result := FWorkDate.Year;
end;

procedure TWorkObject.Init(const AMonth, ADay, AYear: Integer);
begin
  FWorkDate := TWorkDate.Create(AMonth, ADay, AYear);
  Init();
end;

procedure TWorkObject.Init();
begin
  FWorkDate.FDateTime := EncodeDateTime(FWorkDate.Year, FWorkDate.Month, FWorkDate.Day, 0, 0, 0, 0);
end;

end.

Here is test code confirm that the class works okay.

unit WorkObjectTests;

interface

uses
  SysUtils,
  WorkObject,
  TestFrameWork;

const
  MAX_DATES = 4;
  
type
  TWorkObjectTests = class(TTestCase)
  private
    FWorkDates:  array [0..MAX_DATES] of TWorkDate;
    FWorkObjects: array[0..MAX_DATES] of TWorkObject;
  protected

    procedure SetUp; override;
    procedure TearDown; override;

  published

    // Test methods
    procedure TestGetDay;
    procedure TestGetMonth;
    procedure TestGetTodaysDate;
    procedure TestGetYear;

  end;

{  TInitFixture = class
    constructor Create();
    function Init: TWorkObject(WorkObjectTests: TWorkObjectTests);
  end; }

implementation

{ TWorkObjectTests }

procedure TWorkObjectTests.SetUp;
begin
  inherited;
  FWorkDates[0] := TWorkDate.Create(6, 7, 1999);
  FWorkObjects[0] := TWorkObject.Create(FWorkDates[0]);
  FWorkDates[1] := TWorkDate.Create(7, 8, 1900);
  FWorkObjects[1] := TWorkObject.Create(FWorkDates[1]);
  FWorkDates[2] := TWorkDate.Create(8, 9, 1500);
  FWorkObjects[2] := TWorkObject.Create(FWorkDates[2]);
  FWorkDates[3] := TWorkDate.Create(9, 10, 500);
  FWorkObjects[3] := TWorkObject.Create(FWorkDates[3]);
  FWorkDates[4] := TWorkDate.Create(10, 11, 50);
  FWorkObjects[4] := TWorkObject.Create(FWorkDates[4]);
end;

procedure TWorkObjectTests.TearDown;
var
  i: Integer;
begin
  inherited;
  for i := 0 to MAX_DATES do begin
    FreeAndNil(FWorkObjects[i]);
    FreeAndNil(FWorkDates[i]);
  end;
end;

procedure TWorkObjectTests.TestGetDay;
var
  Day, i: Integer;
begin
  for i := 0 to MAX_DATES do begin
    Day := FWorkObjects[i].GetDay;
    CheckEquals(FWorkDates[i].Day, Day);
  end;
end;

procedure TWorkObjectTests.TestGetMonth;
var
  Month, i: Integer;
begin
  for i := 0 to MAX_DATES do begin
    Month := FWorkObjects[i].GetMonth;
    CheckEquals(FWorkDates[i].Month, Month);
  end;
end;

procedure TWorkObjectTests.TestGetTodaysDate;
var
  Date: String;
begin
  Date := FWorkObjects[0].GetTodaysDate();
  Check(Length(Date) <> 0);
end;

procedure TWorkObjectTests.TestGetYear;
var
  Year, i: Integer;
begin
  for i := 0 to MAX_DATES do begin
    Year := FWorkObjects[i].GetYear();
    CheckEquals(FWorkDates[i].Year, Year);
  end;
end;

initialization

  TestFramework.RegisterTest('WorkObjectTests Suite',
    TWorkObjectTests.Suite);

end.

Create List

Use unit tests while constructing a biderectional list object