Spring 2.0: Mock clears expected calls before clearing received calls which leads to an error in strict mode

Issue #405 resolved
Zigmund Bulinsh created an issue

Good day!

The TMockInterceptor.Reset clears expected calls before received calls. This is a problem because the fReceivedCalls collection can have a references to the instances which will released once they are cleared and if the code executed triggers methods on a mock - the error always occurs because the expected calls were just cleared.

The simple project to illustrate the problem (worked with Spring < 2.0):

program Project1;
{$APPTYPE CONSOLE}
uses System.SysUtils, Spring.Collections, Spring.Mocking;
type
  IObserver = interface(IInvokable)
    ['{2CAA5B96-99AD-47CB-8D7A-9D25167C8B05}']
  end;
  ISubject = interface(IInvokable)
    ['{8C8663B4-09D3-4996-A306-016ED6381B11}']
    procedure Attach(AObserver: IObserver);
    procedure Detach(AObserver: IObserver);
  end;
  TService = class(TInterfacedObject, IObserver)
  private
    FSubject: ISubject;
  public
    constructor Create(ASubject: ISubject);
    destructor Destroy; override;
  end;
constructor TService.Create(ASubject: ISubject);
begin
  FSubject := ASubject;
  FSubject.Attach(Self);
end;
destructor TService.Destroy;
begin
  if FSubject <> nil then
    FSubject.Detach(Self);
  inherited;
end;

var
  LSubject: Mock<ISubject>;
  LObserver: IObserver;
begin
  LSubject.Behavior := TMockbehavior.Strict;
  with LSubject.Setup do
  begin
    Executes.When(Args.Any).Attach(nil);
    Executes.When(Args.Any).Detach(nil);
  end;

  LObserver := TService.Create(LSubject);
  LObserver := nil;

  LSubject.Free; // -> This line throws the exception about unexpected invocation
end.

The changes I made which make it working are attached as diff file, but I am not sure if there are any side effects on changing this order.

Comments (2)

  1. Log in to comment