Skip to content

Instantly share code, notes, and snippets.

@drgarcia1986
Last active January 5, 2022 07:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drgarcia1986/10190623 to your computer and use it in GitHub Desktop.
Save drgarcia1986/10190623 to your computer and use it in GitHub Desktop.
This is a example of Dependency Injection and Unit Tests with Delphi-Mocks (https://github.com/VSoftTechnologies/Delphi-Mocks) and Spring4D (https://bitbucket.org/sglienke/spring4d)
unit TestUntORM;
interface
uses
TestFramework,
untORM,
untDB,
untLog,
Delphi.Mocks;
type
[TTableName('tblPerson')]
TPerson = class(TMyORM)
public
[TPrimaryKey]
Id : integer;
Name : String;
Nick : String;
end;
TestTMyORM = class(TTestCase)
strict private
FMyORM: TPerson;
FDataBaseMock : TMock<IDataBase>;
FLogStub : TStub<ILog>;
public
procedure SetUp; override;
procedure TearDown; override;
procedure AfterConstruction; override;
published
procedure TestGet;
end;
implementation
uses
DataSnap.DBClient,
System.Rtti,
Data.DB,
Spring.Container;
procedure TestTMyORM.AfterConstruction;
begin
inherited;
Self.FDataBaseMock := TMock<IDataBase>.Create();
Self.FDataBaseMock.Setup.WillReturn(True).When.Connected();
Self.FLogStub := TStub<ILog>.Create();
GlobalContainer.RegisterType<TPerson>;
GlobalContainer.RegisterType<IDataBase>.DelegateTo(Self.FDataBaseMock.Instance);
GlobalContainer.RegisterType<ILog>.DelegateTo(Self.FLogStub.Instance);
GlobalContainer.Build;
end;
procedure TestTMyORM.SetUp;
begin
FMyORM := GlobalContainer.Resolve<TPerson>;
end;
procedure TestTMyORM.TearDown;
begin
FMyORM.Free;
FMyORM := nil;
end;
procedure TestTMyORM.TestGet;
var
ReturnValue: Boolean;
AId: Integer;
procedure _PrepareMock();
begin
Self.FDataBaseMock.Setup.WillReturnDefault('OpenQuery', TClientDataSet.Create(nil));
Self.FDataBaseMock.Setup.WillExecute(
function (const args : TArray<TValue>; const ReturnType : TRttiType) : TValue
begin
Result := TValue.From<TClientDataSet>(TClientDataSet.Create(nil));
with TClientDataSet(Result.AsObject) do
begin
FieldDefs.Add('Id', ftInteger);
FieldDefs.Add('Name', ftString, 20);
FieldDefs.Add('Nick', ftString, 20);
CreateDataSet();
Append();
FieldByName('Id').Value := AId;
FieldByName('Name').Value := 'Diego';
FieldByName('Nick').Value := 'drgarcia1986';
post();
end;
end
).When.OpenQuery('SELECT Id, Name, Nick FROM tblPerson WHERE Id=1')
end;
begin
AId := 1;
_PrepareMock();
ReturnValue := Self.FMyORM.Get(AId);
CheckTrue(ReturnValue, 'Success on Orm.Get');
CheckEquals(AId, Self.FMyORM.Id, 'Check Equals ID');
CheckEqualsString('Diego', Self.FMyORM.Name, 'Check Equals Name');
CheckEqualsString('drgarcia1986', Self.FMyORM.Nick, 'Check Equals Nick');
ReturnValue := Self.FMyORM.Get(2);
CheckFalse(ReturnValue, 'Expected False because the mock is not prepared to id 2');
end;
initialization
ReportMemoryLeaksOnShutdown := true;
RegisterTest(TestTMyORM.Suite);
end.
unit untDB;
interface
uses
Classes,
DataSnap.DBClient;
type
{$M+}
IDataBase = Interface
['{D197017B-8417-4364-9AED-B3CC00E7B04C}']
function OpenQuery(const AQuery : string):TClientDataSet;
function Connected():Boolean;
function TypeDescription():string;
end;
{$M-}
implementation
end.
unit untLog;
interface
type
{$M+}
ILog = interface
['{B4B12D81-6B1C-4742-A9A8-AFFC758FC239}']
procedure WriteLogFmt(AFormat : string; AArgs : Array of TVarRec);
procedure WriteLog(AMsg : string);
end;
{$M-}
implementation
end.
unit untORM;
interface
uses
untDB,
untLog,
Spring.Services;
type
TPrimaryKey = class(TCustomAttribute);
TTableName = class(TCustomAttribute)
public
Name : string;
constructor Create(const ATableName : string);
end;
TMyORM = class
private
[Inject]
FDataBase : IDataBase;
[Inject]
FLog : ILog;
public
function Get(const AId : Integer):Boolean;
end;
implementation
uses
Classes,
System.SysUtils,
RTTI,
DataSnap.DBClient,
Data.DB,
System.TypInfo;
{ TMyORM }
function TMyORM.Get(const AId: Integer): Boolean;
const
C_QUERY = 'SELECT %s FROM %s WHERE %s=%d';
var
rCtx : TRttiContext;
rTyp : TRttiType;
sQuery: string;
sPkName: string;
oCds : TClientDataSet;
function _GetFieldsNames():string;
var
rFld : TRttiField;
begin
Result := EmptyStr;
for rFld in rTyp.GetFields do
begin
if rFld.Visibility = mvPublic then
begin
if Result = EmptyStr then
Result := rFld.Name
else
Result := Concat(Result,', ',rFld.Name)
end;
end;
end;
function _GetPrimaryKeyName():string;
var
rFld : TRttiField;
oCA : TCustomAttribute;
begin
Result := EmptyStr;
for rFld in rTyp.GetFields do
begin
for oCA in rFld.GetAttributes do
begin
if oCA is TPrimaryKey then
begin
Result := rFld.Name;
Break;
end;
end;
end;
end;
function _PopulateFields():Boolean;
var
I: Integer;
rFld : TRttiField;
begin
if (not oCds.Active) or (oCds.RecordCount = 0) then
Exit(False);
for I := 0 to oCds.FieldDefs.Count - 1 do
begin
rFld := rTyp.GetField(oCds.FieldDefs[I].Name);
case oCds.FieldDefs[i].DataType of
ftString:
rFld.SetValue(Self,TValue.From<string>(oCds.Fields[I].AsString));
ftInteger:
rFld.SetValue(Self,TValue.From<integer>(oCds.Fields[I].AsInteger));
end;
end;
Result := True;
end;
function _GetTableName():String;
var
oCT : TCustomAttribute;
begin
Result := EmptyStr;
for oCT in rTyp.GetAttributes do
begin
if oCT is TTableName then
begin
Result := TTableName(oCT).Name;
Break;
end;
end;
end;
begin
if not Self.FDataBase.Connected() then
begin
Self.FLog.WriteLogFmt('Connection close',[Self.FDataBase.TypeDescription]);
Exit(False);
end;
oCds := nil;
rCtx := TRttiContext.Create();
try
rTyp := rCtx.GetType(Self.ClassType);
sPkName := _GetPrimaryKeyName();
sQuery := Format(C_QUERY,[_GetFieldsNames(), _GetTableName(), sPkName, AId]);
Self.FLog.WriteLogFmt('query generated: [%s]',[sQuery]);
oCds := Self.FDataBase.OpenQuery(sQuery);
Result := _PopulateFields();
finally
rCtx.Free;
if Assigned(oCds) then
oCds.Free;
end;
end;
{ TTableName }
constructor TTableName.Create(const ATableName: string);
begin
Self.Name := ATableName;
end;
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment