Skip to content

Commit 3195d29

Browse files
Merge pull request #130 from mendrix/master
Call original method if behavior is not defined
2 parents f29d2c0 + 0fe6cd6 commit 3195d29

8 files changed

+68
-16
lines changed

Source/Delphi.Mocks.Helpers.pas

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ TRttiTypeHelper = class helper for TRttiType
7777
function FindConstructor : TRttiMethod;
7878
end;
7979

80+
TRttiMethodHelper = class helper for TRttiMethod
81+
function IsAbstract: Boolean;
82+
end;
83+
8084

8185
function CompareValue(const Left, Right: TValue): Integer;
8286
function SameValue(const Left, Right: TValue): Boolean;
@@ -307,4 +311,14 @@ function TRttiTypeHelper.TryGetMethod(const AName: string; out AMethod: TRttiMet
307311
Result := Assigned(AMethod);
308312
end;
309313

314+
{ TRttiMethodHelper }
315+
316+
function TRttiMethodHelper.IsAbstract: Boolean;
317+
begin
318+
if Self = nil then
319+
Result := True
320+
else
321+
Result := PVmtMethodExEntry(Handle).Flags and (1 shl 7) <> 0;
322+
end;
323+
310324
end.

Source/Delphi.Mocks.Interfaces.pas

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ interface
8282

8383
IMethodData = interface
8484
['{640BFB71-85C2-4ED4-A863-5AF6535BD2E8}']
85-
procedure RecordHit(const Args: TArray<TValue>; const returnType : TRttiType; out Result: TValue);
85+
procedure RecordHit(const Args: TArray<TValue>; const returnType : TRttiType; const method : TRttiMethod; out Result: TValue);
8686

8787
//behaviors
8888
procedure WillReturnDefault(const returnValue : TValue);
@@ -114,6 +114,8 @@ interface
114114

115115
//Verification
116116
function Verify(var report : string) : boolean;
117+
118+
function BehaviorDefined: Boolean;
117119
end;
118120

119121
IVerify = interface

Source/Delphi.Mocks.MethodData.pas

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ TMethodData = class(TInterfacedObject,IMethodData)
7070
function FindBehavior(const behaviorType : TBehaviorType; const Args: TArray<TValue>) : IBehavior; overload;
7171
function FindBehavior(const behaviorType : TBehaviorType) : IBehavior; overload;
7272
function FindBestBehavior(const Args: TArray<TValue>) : IBehavior;
73-
procedure RecordHit(const Args: TArray<TValue>; const returnType : TRttiType; out Result : TValue);
73+
procedure RecordHit(const Args: TArray<TValue>; const returnType : TRttiType; const method : TRttiMethod; out Result : TValue);
7474

7575
//Expectations
7676
function FindExpectation(const expectationType : TExpectationType; const Args: TArray<TValue>) : IExpectation;overload;
@@ -96,6 +96,8 @@ TMethodData = class(TInterfacedObject,IMethodData)
9696
procedure After(const AAfterMethodName : string);
9797

9898
function Verify(var report : string) : boolean;
99+
100+
function BehaviorDefined: Boolean;
99101
public
100102
constructor Create(const ATypeName : string; const AMethodName : string; const ASetupParameters: TSetupMethodDataParameters; const AAutoMocker : IAutoMock = nil);
101103
destructor Destroy;override;
@@ -111,7 +113,7 @@ implementation
111113
System.TypInfo,
112114
Delphi.Mocks.Utils,
113115
Delphi.Mocks.Behavior,
114-
Delphi.Mocks.Expectation;
116+
Delphi.Mocks.Expectation, Delphi.Mocks.Helpers;
115117

116118

117119

@@ -263,6 +265,11 @@ function TMethodData.FindExpectation(const expectationTypes : TExpectationTypes)
263265
end;
264266
end;
265267

268+
function TMethodData.BehaviorDefined: Boolean;
269+
begin
270+
Result := (FBehaviors.Count <> 0);
271+
end;
272+
266273
procedure TMethodData.MockNoBehaviourRecordHit(const Args: TArray<TValue>; const AExpectationHitCtr : Integer; const returnType: TRttiType; out Result: TValue);
267274
var
268275
behavior : IBehavior;
@@ -499,7 +506,7 @@ procedure TMethodData.OnceWhen(const Args: TArray<TValue>; const matchers : TArr
499506
end;
500507

501508

502-
procedure TMethodData.RecordHit(const Args: TArray<TValue>; const returnType : TRttiType; out Result: TValue);
509+
procedure TMethodData.RecordHit(const Args: TArray<TValue>; const returnType : TRttiType; const method : TRttiMethod; out Result: TValue);
503510
var
504511
behavior : IBehavior;
505512
expectation : IExpectation;
@@ -519,17 +526,16 @@ procedure TMethodData.RecordHit(const Args: TArray<TValue>; const returnType : T
519526
behavior := FindBestBehavior(Args);
520527
if behavior <> nil then
521528
returnValue := behavior.Execute(Args, returnType)
522-
else
523-
begin
529+
else begin
524530
if FSetupParameters.IsStub then
525531
StubNoBehaviourRecordHit(Args, expectationHitCtr, returnType, returnValue)
526532
else
527-
MockNoBehaviourRecordHit(Args, expectationHitCtr, returnType, returnValue);
533+
if method.IsAbstract then
534+
MockNoBehaviourRecordHit(Args, expectationHitCtr, returnType, returnValue);
528535
end;
529536

530537
if returnType <> nil then
531538
Result := returnValue;
532-
533539
end;
534540

535541
procedure TMethodData.StubNoBehaviourRecordHit(const Args: TArray<TValue>; const AExpectationHitCtr : Integer; const returnType: TRttiType; out Result: TValue);

Source/Delphi.Mocks.ObjectProxy.pas

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,20 @@ procedure TObjectProxy<T>.DoBefore(Instance: TObject; Method: TRttiMethod; const
9797
var
9898
vArgs: TArray<TValue>;
9999
i, l: Integer;
100+
methodData : IMethodData;
101+
pInfo : PTypeInfo;
100102
begin
101103
//don't intercept the TObject methods like BeforeDestruction etc.
102104
if Method.Parent.AsInstance.MetaclassType <> TObject then
103105
begin
104-
DoInvoke := False; //don't call the actual method.
106+
pInfo := TypeInfo(T);
107+
methodData := GetMethodData(method.Name,pInfo.NameStr);
108+
109+
//Call the original (virtual) method if:
110+
//-we are not a stub
111+
//-we have not defined any behavior (of course we count hits)
112+
//-the actual method is not an abstract method
113+
DoInvoke := not (FIsStubOnly or methodData.BehaviorDefined or Method.IsAbstract);
105114

106115
//Included instance as first argument because TExpectation.Match
107116
//deduces that the first argument is the object instance.

Source/Delphi.Mocks.Proxy.TypeInfo.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ procedure TProxy.DoInvoke(Method: TRttiMethod; const Args: TArray<TValue>; out R
333333
//record actual behavior
334334
methodData := GetMethodData(method.Name,pInfo.NameStr);
335335
Assert(methodData <> nil);
336-
methodData.RecordHit(Args,Method.ReturnType,Result);
336+
methodData.RecordHit(Args,Method.ReturnType,Method,Result);
337337
end;
338338
TSetupMode.Behavior:
339339
begin

Source/Delphi.Mocks.Proxy.pas

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ TProxy<T> = class(TWeakReferencedObject, IWeakReferenceableObject, IInterface,
7171
FMethodData : TDictionary<string, IMethodData>;
7272
FBehaviorMustBeDefined : Boolean;
7373
FAllowRedefineBehaviorDefinitions : Boolean;
74-
FSetupMode : TSetupMode;
7574
//behavior setup
7675
FNextBehavior : TBehaviorType;
7776
FReturnValue : TValue;
@@ -83,7 +82,6 @@ TProxy<T> = class(TWeakReferencedObject, IWeakReferenceableObject, IInterface,
8382
FNextExpectation : TExpectationType;
8483
FTimes : Cardinal;
8584
FBetween : array[0..1] of Cardinal;
86-
FIsStubOnly : boolean;
8785

8886
FQueryingInterface : boolean;
8987
FQueryingInternalInterface : boolean;
@@ -106,6 +104,9 @@ TProxyVirtualInterface = class(TVirtualInterface, IInterface, IProxyVirtualI
106104
end;
107105

108106
protected
107+
FSetupMode : TSetupMode;
108+
FIsStubOnly : boolean;
109+
109110
procedure SetParentProxy(const AProxy : IProxy);
110111
function SupportsIInterface: Boolean;
111112

@@ -441,7 +442,7 @@ procedure TProxy<T>.DoInvoke(Method: TRttiMethod; const Args: TArray<TValue>; ou
441442
methodData := GetMethodData(method.Name,pInfo.NameStr);
442443
Assert(methodData <> nil);
443444

444-
methodData.RecordHit(Args,Method.ReturnType,Result);
445+
methodData.RecordHit(Args,Method.ReturnType,Method,Result);
445446
end;
446447
TSetupMode.Behavior:
447448
begin

Tests/Delphi.Mocks.Tests.MethodData.pas

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ procedure TTestMethodData.AllowRedefineBehaviorDefinitions_IsTrue_OldBehaviorIsD
115115
methodData.WillReturnWhen(TArray<TValue>.Create(someValue1), someValue1, nil);
116116
methodData.WillReturnWhen(TArray<TValue>.Create(someValue1), someValue2, nil);
117117

118-
methodData.RecordHit(TArray<TValue>.Create(someValue1), TrttiContext.Create.GetType(TypeInfo(integer)), outValue);
118+
methodData.RecordHit(TArray<TValue>.Create(someValue1), TrttiContext.Create.GetType(TypeInfo(integer)), nil, outValue);
119119

120120
Assert.AreEqual(someValue2.AsInteger, outValue.AsInteger );
121121
end;
@@ -127,7 +127,7 @@ procedure TTestMethodData.BehaviourMustBeDefined_IsFalse_AndBehaviourIsNotDefine
127127
someValue : TValue;
128128
begin
129129
methodData := TMethodData.Create('x', 'x', TSetupMethodDataParameters.Create(FALSE, FALSE, FALSE));
130-
methodData.RecordHit(TArray<TValue>.Create(), nil, someValue);
130+
methodData.RecordHit(TArray<TValue>.Create(), nil, nil, someValue);
131131
// no exception should be raised
132132
Assert.IsTrue(True);
133133
end;
@@ -141,7 +141,7 @@ procedure TTestMethodData.BehaviourMustBeDefined_IsTrue_AndBehaviourIsNotDefined
141141

142142
Assert.WillRaise(procedure
143143
begin
144-
methodData.RecordHit(TArray<TValue>.Create(), TRttiContext.Create.GetType(TypeInfo(Integer)), someValue);
144+
methodData.RecordHit(TArray<TValue>.Create(), TRttiContext.Create.GetType(TypeInfo(Integer)), nil, someValue);
145145
end, EMockException);
146146
end;
147147

Tests/Delphi.Mocks.Tests.ObjectProxy.pas

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ TCommand = class
3232
procedure Run(value: Integer);virtual;abstract;
3333
procedure TestVarParam(var msg : string);virtual;abstract;
3434
procedure TestOutParam(out msg : string);virtual;abstract;
35+
function VirtualMethod: Integer; virtual;
3536
end;
3637

3738
{$M+}
@@ -62,6 +63,8 @@ TTestObjectProxy = class
6263
procedure TestOutParam;
6364
[Test]
6465
procedure TestVarParam;
66+
[Test]
67+
procedure MockNoBehaviorDefined;
6568
end;
6669
{$M-}
6770

@@ -236,6 +239,16 @@ procedure TTestObjectProxy.MockNoArgProcedureUsingOnceWhen;
236239
Assert.Pass;
237240
end;
238241

242+
procedure TTestObjectProxy.MockNoBehaviorDefined;
243+
var
244+
mock : TMock<TCommand>;
245+
begin
246+
mock := TMock<TCommand>.Create;
247+
mock.Setup.Expect.Once.When.VirtualMethod;
248+
Assert.AreEqual(1, mock.Instance.VirtualMethod);
249+
mock.Verify;
250+
end;
251+
239252
procedure TTestObjectProxy.MockWithArgProcedureUsingOnce;
240253
var
241254
mock : TMock<TCommand>;
@@ -267,6 +280,13 @@ constructor TMultipleConstructor.Create;
267280
end;
268281

269282

283+
{ TCommand }
284+
285+
function TCommand.VirtualMethod: Integer;
286+
begin
287+
Result := 1;
288+
end;
289+
270290
initialization
271291
TDUnitX.RegisterTestFixture(TTestObjectProxy);
272292

0 commit comments

Comments
 (0)