Last active
January 5, 2022 07:43
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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